summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2012-03-27 19:52:15 -0400
committerMichael Sevakis <jethead71@rockbox.org>2012-04-29 10:00:56 +0200
commitc9bcbe202d010234f76d1046880a790fe2c3a067 (patch)
tree2f15438664ad1b196d6ed8b78065cd050d351956
parentc9c13497736d8be077663f4458948f7bd526841b (diff)
downloadrockbox-c9bcbe202d010234f76d1046880a790fe2c3a067.tar.gz
rockbox-c9bcbe202d010234f76d1046880a790fe2c3a067.tar.bz2
rockbox-c9bcbe202d010234f76d1046880a790fe2c3a067.zip
Fundamentally rewrite much of the audio DSP.
Creates a standard buffer passing, local data passing and messaging system for processing stages. Stages can be moved to their own source files to reduce clutter and ease assimilation of new ones. dsp.c becomes dsp_core.c which supports an engine and framework for effects. Formats and change notifications are passed along with the buffer so that they arrive at the correct time at each stage in the chain regardless of the internal delays of a particular one. Removes restrictions on the number of samples that can be processed at a time and it pays attention to destination buffer size restrictions without having to limit input count, which also allows pcmbuf to remain fuller and safely set its own buffer limits as it sees fit. There is no longer a need to query input/output counts given a certain number of input samples; just give it the sizes of the source and destination buffers. Works in harmony with stages that are not deterministic in terms of sample input/output ratio (like both resamplers but most notably the timestretch). As a result it fixes quirks with timestretch hanging up with certain settings and it now operates properly throughout its full settings range. Change-Id: Ib206ec78f6f6c79259c5af9009fe021d68be9734 Reviewed-on: http://gerrit.rockbox.org/200 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested-by: Michael Sevakis <jethead71@rockbox.org>
-rw-r--r--apps/codec_thread.c56
-rw-r--r--apps/codecs.c2
-rw-r--r--apps/gui/bitmap/list-skinned.c2
-rw-r--r--apps/gui/bitmap/list.c2
-rw-r--r--apps/gui/option_select.c1
-rw-r--r--apps/main.c7
-rw-r--r--apps/menus/eq_menu.c12
-rw-r--r--apps/pcmbuf.c40
-rw-r--r--apps/plugin.c12
-rw-r--r--apps/plugin.h22
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/mpegplayer/audio_thread.c56
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c8
-rw-r--r--apps/plugins/test_codec.c51
-rw-r--r--apps/settings.c11
-rw-r--r--apps/settings.h18
-rw-r--r--apps/settings_list.c2
-rw-r--r--apps/voice_thread.c37
-rw-r--r--firmware/export/system.h16
-rw-r--r--lib/rbcodec/SOURCES21
-rw-r--r--lib/rbcodec/codecs/codecs.h6
-rw-r--r--lib/rbcodec/codecs/lib/codeclib.c14
-rw-r--r--lib/rbcodec/dsp/channel_mode.c264
-rw-r--r--lib/rbcodec/dsp/channel_mode.h27
-rw-r--r--lib/rbcodec/dsp/compressor.c70
-rw-r--r--lib/rbcodec/dsp/compressor.h14
-rw-r--r--lib/rbcodec/dsp/crossfeed.c214
-rw-r--r--lib/rbcodec/dsp/crossfeed.h28
-rw-r--r--lib/rbcodec/dsp/dsp.c1568
-rw-r--r--lib/rbcodec/dsp/dsp.h212
-rw-r--r--lib/rbcodec/dsp/dsp_arm.S621
-rw-r--r--lib/rbcodec/dsp/dsp_arm_v6.S32
-rw-r--r--lib/rbcodec/dsp/dsp_asm.h86
-rw-r--r--lib/rbcodec/dsp/dsp_cf.S502
-rw-r--r--lib/rbcodec/dsp/dsp_core.c554
-rw-r--r--lib/rbcodec/dsp/dsp_filter.c306
-rw-r--r--lib/rbcodec/dsp/dsp_filter.h57
-rw-r--r--lib/rbcodec/dsp/dsp_misc.c238
-rw-r--r--lib/rbcodec/dsp/dsp_misc.h50
-rw-r--r--lib/rbcodec/dsp/dsp_proc_database.h57
-rw-r--r--lib/rbcodec/dsp/dsp_proc_entry.h153
-rw-r--r--lib/rbcodec/dsp/dsp_proc_settings.h40
-rw-r--r--lib/rbcodec/dsp/dsp_sample_input.c334
-rw-r--r--lib/rbcodec/dsp/dsp_sample_io.h62
-rw-r--r--lib/rbcodec/dsp/dsp_sample_output.c214
-rw-r--r--lib/rbcodec/dsp/eq.c338
-rw-r--r--lib/rbcodec/dsp/eq.h38
-rw-r--r--lib/rbcodec/dsp/eq_arm.S89
-rw-r--r--lib/rbcodec/dsp/eq_cf.S91
-rw-r--r--lib/rbcodec/dsp/lin_resample.c281
-rw-r--r--lib/rbcodec/dsp/pga.c144
-rw-r--r--lib/rbcodec/dsp/pga.h40
-rw-r--r--lib/rbcodec/dsp/tdspeed.c468
-rw-r--r--lib/rbcodec/dsp/tdspeed.h22
-rw-r--r--lib/rbcodec/dsp/tone_controls.c118
-rw-r--r--lib/rbcodec/dsp/tone_controls.h28
56 files changed, 4791 insertions, 2966 deletions
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index 39db741054..17ca980e41 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -213,49 +213,41 @@ void codec_thread_do_callback(void (*fn)(void), unsigned int *id)
static void codec_pcmbuf_insert_callback(
const void *ch1, const void *ch2, int count)
{
- const char *src[2] = { ch1, ch2 };
+ struct dsp_buffer src;
- while (count > 0)
+ src.remcount = count;
+ src.pin[0] = ch1;
+ src.pin[1] = ch2;
+ src.proc_mask = 0;
+
+ while (1)
{
- int out_count = dsp_output_count(ci.dsp, count);
- int inp_count;
- char *dest;
+ struct dsp_buffer dst;
+ dst.remcount = 0;
+ dst.bufcount = MAX(src.remcount, 1024); /* Arbitrary min request */
- while (1)
+ while ((dst.p16out = pcmbuf_request_buffer(&dst.bufcount)) == NULL)
{
- if ((dest = pcmbuf_request_buffer(&out_count)) != NULL)
- break;
-
cancel_cpu_boost();
- /* It will be awhile before space is available but we want
+ /* It may be awhile before space is available but we want
"instant" response to any message */
queue_wait_w_tmo(&codec_queue, NULL, HZ/20);
if (!queue_empty(&codec_queue) &&
codec_check_queue__have_msg() < 0)
+ {
+ dsp_configure(ci.dsp, DSP_FLUSH, 0); /* Discontinuity */
return;
+ }
}
- /* Get the real input_size for output_size bytes, guarding
- * against resampling buffer overflows. */
- inp_count = dsp_input_count(ci.dsp, out_count);
-
- if (inp_count <= 0)
- return;
-
- /* Input size has grown, no error, just don't write more than length */
- if (inp_count > count)
- inp_count = count;
+ dsp_process(ci.dsp, &src, &dst);
- out_count = dsp_process(ci.dsp, dest, src, inp_count);
-
- if (out_count <= 0)
- return;
-
- pcmbuf_write_complete(out_count, ci.id3->elapsed, ci.id3->offset);
-
- count -= inp_count;
+ if (dst.remcount > 0)
+ pcmbuf_write_complete(dst.remcount, ci.id3->elapsed, ci.id3->offset);
+ else if (src.remcount <= 0)
+ break; /* No input remains and DSP purged */
}
}
@@ -352,10 +344,7 @@ static void codec_seek_complete_callback(void)
static void codec_configure_callback(int setting, intptr_t value)
{
- if (!dsp_configure(ci.dsp, setting, value))
- {
- logf("Illegal key: %d", setting);
- }
+ dsp_configure(ci.dsp, setting, value);
}
static enum codec_command_action
@@ -611,8 +600,7 @@ static void NORETURN_ATTR codec_thread(void)
void codec_thread_init(void)
{
/* Init API */
- ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
- CODEC_IDX_AUDIO);
+ ci.dsp = dsp_get_config(CODEC_IDX_AUDIO);
ci.codec_get_buffer = codec_get_buffer_callback;
ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
ci.set_elapsed = audio_codec_update_elapsed;
diff --git a/apps/codecs.c b/apps/codecs.c
index fafe4ac7a3..69204b7c4f 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -118,6 +118,7 @@ struct codec_api ci = {
commit_dcache,
commit_discard_dcache,
+ commit_discard_idcache,
/* strings and memory */
strcpy,
@@ -166,7 +167,6 @@ struct codec_api ci = {
/* new stuff at the end, sort into place next time
the API gets incompatible */
- commit_discard_idcache,
};
void codec_get_full_path(char *path, const char *codec_root_fn)
diff --git a/apps/gui/bitmap/list-skinned.c b/apps/gui/bitmap/list-skinned.c
index 95430ae278..7d3620ed81 100644
--- a/apps/gui/bitmap/list-skinned.c
+++ b/apps/gui/bitmap/list-skinned.c
@@ -20,13 +20,13 @@
****************************************************************************/
#include "config.h"
+#include "system.h"
#include "lcd.h"
#include "font.h"
#include "button.h"
#include "string.h"
#include "settings.h"
#include "kernel.h"
-#include "system.h"
#include "file.h"
#include "action.h"
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index 0581050091..49479c1cb3 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -22,13 +22,13 @@
/* This file contains the code to draw the list widget on BITMAP LCDs. */
#include "config.h"
+#include "system.h"
#include "lcd.h"
#include "font.h"
#include "button.h"
#include "string.h"
#include "settings.h"
#include "kernel.h"
-#include "system.h"
#include "file.h"
#include "action.h"
diff --git a/apps/gui/option_select.c b/apps/gui/option_select.c
index 7c3d34f024..ca206b86da 100644
--- a/apps/gui/option_select.c
+++ b/apps/gui/option_select.c
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include "string-extra.h"
#include "config.h"
+#include "system.h"
#include "option_select.h"
#include "kernel.h"
#include "lang.h"
diff --git a/apps/main.c b/apps/main.c
index 631236852e..0fff0846a6 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -384,6 +384,9 @@ static void init(void)
viewportmanager_init();
storage_init();
+#if CONFIG_CODEC == SWCODEC
+ dsp_init();
+#endif
settings_reset();
settings_load(SETTINGS_ALL);
settings_apply(true);
@@ -632,6 +635,10 @@ static void init(void)
}
}
+#if CONFIG_CODEC == SWCODEC
+ dsp_init();
+#endif
+
#if defined(SETTINGS_RESET) || (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IRIVER_H10_PAD)
#ifdef SETTINGS_RESET
diff --git a/apps/menus/eq_menu.c b/apps/menus/eq_menu.c
index 2cfb80f76a..60b2687050 100644
--- a/apps/menus/eq_menu.c
+++ b/apps/menus/eq_menu.c
@@ -70,13 +70,11 @@ const char* eq_precut_format(char* buffer, size_t buffer_size, int value, const
*/
static void eq_apply(void)
{
- dsp_set_eq(global_settings.eq_enabled);
+ dsp_eq_enable(global_settings.eq_enabled);
dsp_set_eq_precut(global_settings.eq_precut);
/* Update all bands */
- for(int i = 0; i < 5; i++) {
- dsp_set_eq_coefs(i, global_settings.eq_band_settings[i].cutoff,
- global_settings.eq_band_settings[i].q,
- global_settings.eq_band_settings[i].gain);
+ for(int i = 0; i < EQ_NUM_BANDS; i++) {
+ dsp_set_eq_coefs(i, &global_settings.eq_band_settings[i]);
}
}
@@ -580,9 +578,7 @@ bool eq_menu_graphical(void)
/* Update the filter if the user changed something */
if (has_changed) {
dsp_set_eq_coefs(current_band,
- global_settings.eq_band_settings[current_band].cutoff,
- global_settings.eq_band_settings[current_band].q,
- global_settings.eq_band_settings[current_band].gain);
+ &global_settings.eq_band_settings[current_band]);
has_changed = false;
}
}
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index d36883fc5b..9cedae0b67 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -47,16 +47,23 @@
smaller math - must be < 65536 bytes */
#define PCMBUF_CHUNK_SIZE 8192u
-/* Massive size is a nasty temp fix */
-#define PCMBUF_GUARD_SIZE (1024u*12*((NATIVE_FREQUENCY+7999)/8000))
+/* Small guard buf to give decent space near end */
+#define PCMBUF_GUARD_SIZE (PCMBUF_CHUNK_SIZE / 8)
/* Mnemonics for common data commit thresholds */
#define COMMIT_CHUNKS PCMBUF_CHUNK_SIZE
#define COMMIT_ALL_DATA 1u
- /* Size of the crossfade buffer where codec data is written to be faded
- on commit */
-#define CROSSFADE_BUFSIZE 8192u
+/* Size of the crossfade buffer where codec data is written to be faded
+ on commit */
+#define CROSSFADE_BUFSIZE PCMBUF_CHUNK_SIZE
+
+/* Maximum contiguous space that PCM buffer will allow (to avoid excessive
+ draining between inserts and observe low-latency mode) */
+#define PCMBUF_MAX_BUFFER (PCMBUF_CHUNK_SIZE * 4)
+
+/* Forced buffer insert constraint can thus be from 1KB to 32KB using 8KB
+ chunks */
/* Return data level in 1/4-second increments */
#define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs))
@@ -383,7 +390,11 @@ void * pcmbuf_request_buffer(int *count)
/* If crossfade has begun, put the new track samples in crossfade_buffer */
if (crossfade_status != CROSSFADE_INACTIVE && size > CROSSFADE_BUFSIZE)
size = CROSSFADE_BUFSIZE;
-#endif
+ else
+#endif /* HAVE_CROSSFADE */
+
+ if (size > PCMBUF_MAX_BUFFER)
+ size = PCMBUF_MAX_BUFFER; /* constrain */
enum channel_status status = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK);
size_t remaining = pcmbuf_unplayed_bytes();
@@ -432,11 +443,22 @@ void * pcmbuf_request_buffer(int *count)
pcmbuf_play_start();
}
- void *buf =
+ void *buf;
+
#ifdef HAVE_CROSSFADE
- crossfade_status != CROSSFADE_INACTIVE ? crossfade_buffer :
+ if (crossfade_status != CROSSFADE_INACTIVE)
+ {
+ buf = crossfade_buffer; /* always CROSSFADE_BUFSIZE */
+ }
+ else
#endif
- get_write_buffer(&size);
+ {
+ /* Give the maximum amount available if there's more */
+ if (size + PCMBUF_CHUNK_SIZE < freespace)
+ size = freespace - PCMBUF_CHUNK_SIZE;
+
+ buf = get_write_buffer(&size);
+ }
*count = size / 4;
return buf;
diff --git a/apps/plugin.c b/apps/plugin.c
index 5406610e23..129fd6954e 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -565,13 +565,15 @@ static const struct plugin_api rockbox_api = {
audio_set_output_source,
audio_set_input_source,
#endif
- dsp_set_crossfeed,
- dsp_set_eq,
+ dsp_crossfeed_enable,
+ dsp_eq_enable,
dsp_dither_enable,
+#ifdef HAVE_PITCHSCREEN
+ dsp_set_timestretch,
+#endif
dsp_configure,
+ dsp_get_config,
dsp_process,
- dsp_input_count,
- dsp_output_count,
mixer_channel_status,
mixer_channel_get_buffer,
@@ -584,7 +586,7 @@ static const struct plugin_api rockbox_api = {
system_sound_play,
keyclick_click,
-#endif
+#endif /* CONFIG_CODEC == SWCODEC */
/* playback control */
playlist_amount,
playlist_resume,
diff --git a/apps/plugin.h b/apps/plugin.h
index 8123414ff0..3820c7ede6 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -153,12 +153,12 @@ void* plugin_get_buffer(size_t *buffer_size);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 218
+#define PLUGIN_API_VERSION 219
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */
-#define PLUGIN_MIN_API_VERSION 218
+#define PLUGIN_MIN_API_VERSION 219
/* plugin return codes */
/* internal returns start at 0x100 to make exit(1..255) work */
@@ -680,15 +680,17 @@ struct plugin_api {
void (*audio_set_output_source)(int monitor);
void (*audio_set_input_source)(int source, unsigned flags);
#endif
- void (*dsp_set_crossfeed)(bool enable);
- void (*dsp_set_eq)(bool enable);
+ void (*dsp_crossfeed_enable)(bool enable);
+ void (*dsp_eq_enable)(bool enable);
void (*dsp_dither_enable)(bool enable);
- intptr_t (*dsp_configure)(struct dsp_config *dsp, int setting,
- intptr_t value);
- int (*dsp_process)(struct dsp_config *dsp, char *dest,
- const char *src[], int count);
- int (*dsp_input_count)(struct dsp_config *dsp, int count);
- int (*dsp_output_count)(struct dsp_config *dsp, int count);
+#ifdef HAVE_PITCHSCREEN
+ void (*dsp_set_timestretch)(int32_t percent);
+#endif
+ intptr_t (*dsp_configure)(struct dsp_config *dsp,
+ unsigned int setting, intptr_t value);
+ struct dsp_config * (*dsp_get_config)(enum dsp_ids id);
+ void (*dsp_process)(struct dsp_config *dsp, struct dsp_buffer *src,
+ struct dsp_buffer *dst);
enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel);
const void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel,
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index db690a638a..e5f026c5b4 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -33,6 +33,7 @@ flipit.c
shopper.c
resistor.c
+test_codec.c
#ifdef USB_ENABLE_HID
remote_control.c
diff --git a/apps/plugins/mpegplayer/audio_thread.c b/apps/plugins/mpegplayer/audio_thread.c
index f976fd6007..b06727f759 100644
--- a/apps/plugins/mpegplayer/audio_thread.c
+++ b/apps/plugins/mpegplayer/audio_thread.c
@@ -36,6 +36,7 @@ struct audio_thread_data
unsigned samplerate; /* Current stream sample rate */
int nchannels; /* Number of audio channels */
struct dsp_config *dsp; /* The DSP we're using */
+ struct dsp_buffer src; /* Current audio data for DSP processing */
};
/* The audio thread is stolen from the core codec thread */
@@ -479,12 +480,13 @@ static void audio_thread(void)
/* We need this here to init the EMAC for Coldfire targets */
init_mad();
- td.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
- CODEC_IDX_AUDIO);
+ td.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
#ifdef HAVE_PITCHSCREEN
rb->sound_set_pitch(PITCH_SPEED_100);
+ rb->dsp_set_timestretch(PITCH_SPEED_100);
#endif
rb->dsp_configure(td.dsp, DSP_RESET, 0);
+ rb->dsp_configure(td.dsp, DSP_FLUSH, 0);
rb->dsp_configure(td.dsp, DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
goto message_wait;
@@ -631,43 +633,53 @@ static void audio_thread(void)
STEREO_MONO : STEREO_NONINTERLEAVED);
}
+ td.src.remcount = synth.pcm.length;
+ td.src.pin[0] = synth.pcm.samples[0];
+ td.src.pin[1] = synth.pcm.samples[1];
+ td.src.proc_mask = 0;
+
td.state = TSTATE_RENDER_WAIT;
/* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */
render_wait:
- if (synth.pcm.length > 0)
+ rb->yield();
+
+ while (1)
{
- const char *src[2] =
- { (char *)synth.pcm.samples[0], (char *)synth.pcm.samples[1] };
- int out_count = (synth.pcm.length * CLOCK_RATE
- + (td.samplerate - 1)) / td.samplerate;
- unsigned char *out_buf;
- ssize_t size = out_count*4;
+ struct dsp_buffer dst;
+ dst.remcount = 0;
+ dst.bufcount = MAX(td.src.remcount, 1024);
+
+ ssize_t size = dst.bufcount * 2 * sizeof(int16_t);
/* Wait for required amount of free buffer space */
- while ((out_buf = pcm_output_get_buffer(&size)) == NULL)
+ while ((dst.p16out = pcm_output_get_buffer(&size)) == NULL)
{
/* Wait one frame */
- int timeout = out_count*HZ / td.samplerate;
+ int timeout = dst.bufcount*HZ / td.samplerate;
str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1));
if (td.ev.id != SYS_TIMEOUT)
goto message_process;
}
- out_count = rb->dsp_process(td.dsp, out_buf, src, synth.pcm.length);
+ dst.bufcount = size / (2 * sizeof (int16_t));
+ rb->dsp_process(td.dsp, &td.src, &dst);
- if (out_count <= 0)
- break;
-
- /* Make this data available to DMA */
- pcm_output_commit_data(out_count*4, audio_queue.curr->time);
+ if (dst.remcount > 0)
+ {
+ /* Make this data available to DMA */
+ pcm_output_commit_data(dst.remcount * 2 * sizeof(int16_t),
+ audio_queue.curr->time);
- /* As long as we're on this timestamp, the time is just
- incremented by the number of samples */
- audio_queue.curr->time += out_count;
+ /* As long as we're on this timestamp, the time is just
+ incremented by the number of samples */
+ audio_queue.curr->time += dst.remcount;
+ }
+ else if (td.src.remcount <= 0)
+ {
+ break;
+ }
}
-
- rb->yield();
} /* end decoding loop */
}
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
index 1c3f3c0b92..7f92fb7c69 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.c
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -457,13 +457,13 @@ static void sync_audio_setting(int setting, bool global)
break;
case MPEG_AUDIO_CROSSFEED:
- rb->dsp_set_crossfeed((global || settings.crossfeed) ?
- rb->global_settings->crossfeed : false);
+ rb->dsp_crossfeed_enable((global || settings.crossfeed) ?
+ rb->global_settings->crossfeed : false);
break;
case MPEG_AUDIO_EQUALIZER:
- rb->dsp_set_eq((global || settings.equalizer) ?
- rb->global_settings->eq_enabled : false);
+ rb->dsp_eq_enable((global || settings.equalizer) ?
+ rb->global_settings->eq_enabled : false);
break;
case MPEG_AUDIO_DITHERING:
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index dafcf35710..920be54d56 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -164,6 +164,7 @@ static inline void int2le16(unsigned char* buf, int16_t x)
static unsigned char *wavbuffer;
static unsigned char *dspbuffer;
+static int dspbuffer_count;
void init_wav(char* filename)
{
@@ -215,34 +216,31 @@ static void* codec_get_buffer(size_t *size)
static int process_dsp(const void *ch1, const void *ch2, int count)
{
- const char *src[2] = { ch1, ch2 };
- int written_count = 0;
- char *dest = dspbuffer;
-
- while (count > 0)
+ struct dsp_buffer src;
+ src.remcount = count;
+ src.pin[0] = ch1;
+ src.pin[1] = ch2;
+ src.proc_mask = 0;
+
+ struct dsp_buffer dst;
+ dst.remcount = 0;
+ dst.p16out = (int16_t *)dspbuffer;
+ dst.bufcount = dspbuffer_count;
+
+ while (1)
{
- int out_count = rb->dsp_output_count(ci.dsp, count);
-
- int inp_count = rb->dsp_input_count(ci.dsp, out_count);
-
- if (inp_count <= 0)
- break;
-
- if (inp_count > count)
- inp_count = count;
-
- out_count = rb->dsp_process(ci.dsp, dest, src, inp_count);
+ int old_remcount = dst.remcount;
+ rb->dsp_process(ci.dsp, &src, &dst);
- if (out_count <= 0)
+ if (dst.bufcount <= 0 ||
+ (src.remcount <= 0 && dst.remcount <= old_remcount))
+ {
+ /* Dest is full or no input left and DSP purged */
break;
-
- written_count += out_count;
- dest += out_count * 4;
-
- count -= inp_count;
+ }
}
- return written_count;
+ return dst.remcount;
}
/* Null output */
@@ -502,7 +500,6 @@ static void configure(int setting, intptr_t value)
rb->dsp_configure(ci.dsp, setting, value);
switch(setting)
{
- case DSP_SWITCH_FREQUENCY:
case DSP_SET_FREQUENCY:
DEBUGF("samplerate=%d\n",(int)value);
wavinfo.samplerate = use_dsp ? NATIVE_FREQUENCY : (int)value;
@@ -525,9 +522,7 @@ static void init_ci(void)
{
/* --- Our "fake" implementations of the codec API functions. --- */
- ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
- CODEC_IDX_AUDIO);
-
+ ci.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
ci.codec_get_buffer = codec_get_buffer;
if (wavinfo.fd >= 0 || checksum) {
@@ -849,6 +844,8 @@ enum plugin_status plugin_start(const void* parameter)
wavbuffer = rb->plugin_get_buffer(&buffer_size);
dspbuffer = wavbuffer + buffer_size / 2;
+ dspbuffer_count = (buffer_size - (dspbuffer - wavbuffer)) /
+ (2 * sizeof (int16_t));
codec_mallocbuf = rb->plugin_get_audio_buffer(&audiosize);
/* Align codec_mallocbuf to pointer size, tlsf wants that */
diff --git a/apps/settings.c b/apps/settings.c
index acc38c2388..49d239a2c1 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -979,20 +979,17 @@ void settings_apply(bool read_disk)
audio_set_crossfade(global_settings.crossfade);
#endif
dsp_set_replaygain();
- dsp_set_crossfeed(global_settings.crossfeed);
+ dsp_crossfeed_enable(global_settings.crossfeed);
dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain);
dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
global_settings.crossfeed_hf_attenuation,
global_settings.crossfeed_hf_cutoff);
/* Configure software equalizer, hardware eq is handled in audio_init() */
- dsp_set_eq(global_settings.eq_enabled);
+ dsp_eq_enable(global_settings.eq_enabled);
dsp_set_eq_precut(global_settings.eq_precut);
-
- for(int i = 0; i < 5; i++) {
- dsp_set_eq_coefs(i, global_settings.eq_band_settings[i].cutoff,
- global_settings.eq_band_settings[i].q,
- global_settings.eq_band_settings[i].gain);
+ for(int i = 0; i < EQ_NUM_BANDS; i++) {
+ dsp_set_eq_coefs(i, &global_settings.eq_band_settings[i]);
}
dsp_dither_enable(global_settings.dithering_enabled);
diff --git a/apps/settings.h b/apps/settings.h
index b312c1e784..4d94ca8ba8 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -32,6 +32,7 @@
#include "button.h"
#if CONFIG_CODEC == SWCODEC
#include "audio.h"
+#include "dsp.h"
#endif
#include "rbpaths.h"
@@ -339,13 +340,7 @@ struct user_settings
/* EQ */
bool eq_enabled; /* Enable equalizer */
unsigned int eq_precut; /* dB */
-
- struct eq_band_setting
- {
- int cutoff; /* Hz */
- int q;
- int gain; /* +/- dB */
- } eq_band_settings[5];
+ struct eq_band_setting eq_band_settings[EQ_NUM_BANDS]; /* for each band */
/* Misc. swcodec */
int beep; /* system beep volume when changing tracks etc. */
@@ -772,14 +767,7 @@ struct user_settings
#endif
#if CONFIG_CODEC == SWCODEC
- struct compressor_settings
- {
- int threshold;
- int makeup_gain;
- int ratio;
- int knee;
- int release_time;
- } compressor_settings;
+ struct compressor_settings compressor_settings;
#endif
int sleeptimer_duration; /* In minutes; 0=off */
diff --git a/apps/settings_list.c b/apps/settings_list.c
index af48d11c85..82cccd891f 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1398,7 +1398,7 @@ const struct settings_list settings[] = {
/* crossfeed */
OFFON_SETTING(F_SOUNDSETTING, crossfeed, LANG_CROSSFEED, false,
- "crossfeed", dsp_set_crossfeed),
+ "crossfeed", dsp_crossfeed_enable),
INT_SETTING_NOWRAP(F_SOUNDSETTING, crossfeed_direct_gain,
LANG_CROSSFEED_DIRECT_GAIN, -15,
"crossfeed direct gain", UNIT_DB, -60, 0, 5,
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 07a67256c4..cff703adfa 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -133,9 +133,8 @@ struct voice_thread_data
SpeexBits bits; /* Bit cursor */
struct dsp_config *dsp; /* DSP used for voice output */
struct voice_info vi; /* Copy of clip data */
- const char *src[2]; /* Current output buffer pointers */
int lookahead; /* Number of samples to drop at start of clip */
- int count; /* Count of samples remaining to send to PCM */
+ struct dsp_buffer src; /* Speex output buffer/input to DSP */
};
/* Functions called in their repective state that return the next state to
@@ -264,9 +263,7 @@ void voice_wait(void)
* setup the DSP parameters */
static void voice_data_init(struct voice_thread_data *td)
{
- td->dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
- CODEC_IDX_VOICE);
-
+ td->dsp = dsp_get_config(CODEC_IDX_VOICE);
dsp_configure(td->dsp, DSP_RESET, 0);
dsp_configure(td->dsp, DSP_SET_FREQUENCY, VOICE_SAMPLE_RATE);
dsp_configure(td->dsp, DSP_SET_SAMPLE_DEPTH, VOICE_SAMPLE_DEPTH);
@@ -378,7 +375,8 @@ static enum voice_state voice_decode(struct voice_thread_data *td)
else
{
/* If all clips are done and not playing, force pcm playback. */
- voice_start_playback();
+ if (voice_unplayed_frames() > 0)
+ voice_start_playback();
return VOICE_STATE_MESSAGE;
}
}
@@ -387,12 +385,14 @@ static enum voice_state voice_decode(struct voice_thread_data *td)
yield();
/* Output the decoded frame */
- td->count = VOICE_FRAME_COUNT - td->lookahead;
- td->src[0] = (const char *)&voice_output_buf[td->lookahead];
- td->src[1] = NULL;
+ td->src.remcount = VOICE_FRAME_COUNT - td->lookahead;
+ td->src.pin[0] = &voice_output_buf[td->lookahead];
+ td->src.pin[1] = NULL;
+ td->src.proc_mask = 0;
+
td->lookahead -= MIN(VOICE_FRAME_COUNT, td->lookahead);
- if (td->count > 0)
+ if (td->src.remcount > 0)
return VOICE_STATE_BUFFER_INSERT;
}
@@ -405,12 +405,21 @@ static enum voice_state voice_buffer_insert(struct voice_thread_data *td)
if (!queue_empty(&voice_queue))
return VOICE_STATE_MESSAGE;
- char *dest = (char *)voice_buf_get();
+ struct dsp_buffer dst;
- if (dest != NULL)
+ if ((dst.p16out = voice_buf_get()) != NULL)
{
- voice_buf_commit(dsp_process(td->dsp, dest, td->src, td->count));
- return VOICE_STATE_DECODE;
+ dst.remcount = 0;
+ dst.bufcount = VOICE_PCM_FRAME_COUNT;
+
+ dsp_process(td->dsp, &td->src, &dst);
+
+ voice_buf_commit(dst.remcount);
+
+ /* Unless other effects are introduced to voice that have delays,
+ all output should have been purged to dst in one call */
+ return td->src.remcount > 0 ?
+ VOICE_STATE_BUFFER_INSERT : VOICE_STATE_DECODE;
}
sleep(0);
diff --git a/firmware/export/system.h b/firmware/export/system.h
index d93d10c9e2..b1959c496d 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -300,6 +300,12 @@ static inline uint32_t swaw32_hw(uint32_t value)
#define BIT_N(n) (1U << (n))
#endif
+#ifndef MASK_N
+/* Make a mask of n contiguous bits, shifted left by 'shift' */
+#define MASK_N(type, n, shift) \
+ ((type)((((type)1 << (n)) - (type)1) << (shift)))
+#endif
+
/* Declare this as HIGHEST_IRQ_LEVEL if they don't differ */
#ifndef DISABLE_INTERRUPTS
#define DISABLE_INTERRUPTS HIGHEST_IRQ_LEVEL
@@ -352,7 +358,7 @@ static inline uint32_t swaw32_hw(uint32_t value)
/* Define MEM_ALIGN_ATTR which may be used to align e.g. buffers for faster
* access. */
-#if defined(CPU_ARM)
+#if defined(CPU_ARM)
/* Use ARMs cache alignment. */
#define MEM_ALIGN_ATTR CACHEALIGN_ATTR
#define MEM_ALIGN_SIZE CACHEALIGN_SIZE
@@ -361,12 +367,16 @@ static inline uint32_t swaw32_hw(uint32_t value)
#define MEM_ALIGN_ATTR __attribute__((aligned(16)))
#define MEM_ALIGN_SIZE 16
#else
- /* Do nothing. */
- #define MEM_ALIGN_ATTR
/* Align pointer size */
+ #define MEM_ALIGN_ATTR __attribute__((aligned(sizeof(intptr_t))))
#define MEM_ALIGN_SIZE sizeof(intptr_t)
#endif
+#define MEM_ALIGN_UP(x) \
+ ((typeof (x))ALIGN_UP((uintptr_t)(x), MEM_ALIGN_SIZE))
+#define MEM_ALIGN_DOWN(x) \
+ ((typeof (x))ALIGN_DOWN((uintptr_t)(x), MEM_ALIGN_SIZE))
+
#ifdef STORAGE_WANTS_ALIGN
#define STORAGE_ALIGN_ATTR __attribute__((aligned(CACHEALIGN_SIZE)))
#define STORAGE_ALIGN_DOWN(x) \
diff --git a/lib/rbcodec/SOURCES b/lib/rbcodec/SOURCES
index 3ac2660a38..c293f3c028 100644
--- a/lib/rbcodec/SOURCES
+++ b/lib/rbcodec/SOURCES
@@ -3,22 +3,31 @@ metadata/id3tags.c
metadata/mp3.c
metadata/mp3data.c
#if CONFIG_CODEC == SWCODEC
+dsp/channel_mode.c
dsp/compressor.c
-dsp/dsp.c
+dsp/crossfeed.c
+dsp/dsp_core.c
+dsp/dsp_filter.c
+dsp/dsp_misc.c
+dsp/dsp_sample_input.c
+dsp/dsp_sample_output.c
dsp/eq.c
+dsp/lin_resample.c
+dsp/pga.c
+# ifdef HAVE_PITCHSCREEN
+dsp/tdspeed.c
+# endif
+# ifdef HAVE_SW_TONE_CONTROLS
+dsp/tone_controls.c
+# endif
# if defined(CPU_COLDFIRE)
dsp/dsp_cf.S
-dsp/eq_cf.S
# elif defined(CPU_ARM)
dsp/dsp_arm.S
-dsp/eq_arm.S
# if ARM_ARCH >= 6
dsp/dsp_arm_v6.S
# endif
# endif
-# ifdef HAVE_PITCHSCREEN
-dsp/tdspeed.c
-# endif
metadata/replaygain.c
metadata/metadata_common.c
metadata/a52.c
diff --git a/lib/rbcodec/codecs/codecs.h b/lib/rbcodec/codecs/codecs.h
index bad8cdd469..d4a51a4aba 100644
--- a/lib/rbcodec/codecs/codecs.h
+++ b/lib/rbcodec/codecs/codecs.h
@@ -75,12 +75,12 @@
#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
/* increase this every time the api struct changes */
-#define CODEC_API_VERSION 44
+#define CODEC_API_VERSION 45
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */
-#define CODEC_MIN_API_VERSION 43
+#define CODEC_MIN_API_VERSION 45
/* reasons for calling codec main entrypoint */
enum codec_entry_call_reason {
@@ -171,6 +171,7 @@ struct codec_api {
void (*commit_dcache)(void);
void (*commit_discard_dcache)(void);
+ void (*commit_discard_idcache)(void);
/* strings and memory */
char* (*strcpy)(char *dst, const char *src);
@@ -223,7 +224,6 @@ struct codec_api {
/* new stuff at the end, sort into place next time
the API gets incompatible */
- void (*commit_discard_idcache)(void);
};
/* codec header */
diff --git a/lib/rbcodec/codecs/lib/codeclib.c b/lib/rbcodec/codecs/lib/codeclib.c
index 36f4279941..4ca6c8c993 100644
--- a/lib/rbcodec/codecs/lib/codeclib.c
+++ b/lib/rbcodec/codecs/lib/codeclib.c
@@ -26,6 +26,7 @@
#include "dsp.h"
#include "codeclib.h"
#include "metadata.h"
+#include "dsp_proc_entry.h"
/* The following variables are used by codec_malloc() to make use of free RAM
* within the statically allocated codec buffer. */
@@ -44,10 +45,15 @@ int codec_init(void)
void codec_set_replaygain(const struct mp3entry *id3)
{
- ci->configure(DSP_SET_TRACK_GAIN, id3->track_gain);
- ci->configure(DSP_SET_ALBUM_GAIN, id3->album_gain);
- ci->configure(DSP_SET_TRACK_PEAK, id3->track_peak);
- ci->configure(DSP_SET_ALBUM_PEAK, id3->album_peak);
+ struct dsp_replay_gains gains =
+ {
+ .track_gain = id3->track_gain,
+ .album_gain = id3->album_gain,
+ .track_peak = id3->track_peak,
+ .album_peak = id3->album_peak,
+ };
+
+ ci->configure(REPLAYGAIN_SET_GAINS, (intptr_t)&gains);
}
/* Various "helper functions" common to all the xxx2wav decoder plugins */
diff --git a/lib/rbcodec/dsp/channel_mode.c b/lib/rbcodec/dsp/channel_mode.c
new file mode 100644
index 0000000000..5b678887c2
--- /dev/null
+++ b/lib/rbcodec/dsp/channel_mode.c
@@ -0,0 +1,264 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "dsp.h"
+#include "settings.h"
+#include "sound.h"
+#include "fixedpoint.h"
+#include "fracmul.h"
+#include "dsp_proc_entry.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);
diff --git a/lib/rbcodec/dsp/channel_mode.h b/lib/rbcodec/dsp/channel_mode.h
new file mode 100644
index 0000000000..7ca0f74204
--- /dev/null
+++ b/lib/rbcodec/dsp/channel_mode.h
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Thom Johansen
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef CHANNEL_MODE_H
+#define CHANNEL_MODE_H
+
+void channel_mode_set_config(int value);
+void channel_mode_custom_set_width(int value);
+
+#endif /* CHANNEL_MODE_H */
diff --git a/lib/rbcodec/dsp/compressor.c b/lib/rbcodec/dsp/compressor.c
index a6c1ac1018..1816bfef9c 100644
--- a/lib/rbcodec/dsp/compressor.c
+++ b/lib/rbcodec/dsp/compressor.c
@@ -19,16 +19,18 @@
*
****************************************************************************/
#include "config.h"
+#include "system.h"
#include "fixedpoint.h"
#include "fracmul.h"
-#include "settings.h"
#include "dsp.h"
-#include "compressor.h"
+#include <string.h>
/* Define LOGF_ENABLE to enable logf output in this file */
/*#define LOGF_ENABLE*/
#include "logf.h"
+#include "dsp_proc_entry.h"
+
static struct compressor_settings curr_set; /* Cached settings */
static int32_t comp_rel_slope IBSS_ATTR; /* S7.24 format */
@@ -251,10 +253,10 @@ bool compressor_update(const struct compressor_settings *settings)
* Returns the required gain factor in S7.24 format in order to compress the
* sample in accordance with the compression curve. Always 1 or less.
*/
-static inline int32_t get_compression_gain(struct dsp_data *data,
+static inline int32_t get_compression_gain(struct sample_format *format,
int32_t sample)
{
- const int frac_bits_offset = data->frac_bits - 15;
+ const int frac_bits_offset = format->frac_bits - 15;
/* sample must be positive */
if (sample < 0)
@@ -292,24 +294,40 @@ static inline int32_t get_compression_gain(struct dsp_data *data,
return -1;
}
+/** DSP interface **/
+
+/** SET COMPRESSOR
+ * Enable or disable the compressor based upon the settings
+ */
+void dsp_set_compressor(const struct compressor_settings *settings)
+{
+ /* enable/disable the compressor depending upon settings */
+ bool enable = compressor_update(settings);
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ dsp_proc_enable(dsp, DSP_PROC_COMPRESSOR, enable);
+ dsp_proc_activate(dsp, DSP_PROC_COMPRESSOR, true);
+}
+
/** COMPRESSOR PROCESS
* Changes the gain of the samples according to the compressor curve
*/
-void compressor_process(int count, struct dsp_data *data, int32_t *buf[])
+static void compressor_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
{
- const int num_chan = data->num_channels;
- int32_t *in_buf[2] = {buf[0], buf[1]};
-
+ struct dsp_buffer *buf = *buf_p;
+ int count = buf->remcount;
+ int32_t *in_buf[2] = { buf->p32[0], buf->p32[1] };
+ const int num_chan = buf->format.num_channels;
+
while (count-- > 0)
{
- int ch;
/* use lowest (most compressed) gain factor of the output buffer
sample pair for both samples (mono is also handled correctly here)
*/
int32_t sample_gain = UNITY;
- for (ch = 0; ch < num_chan; ch++)
+ for (int ch = 0; ch < num_chan; ch++)
{
- int32_t this_gain = get_compression_gain(data, *in_buf[ch]);
+ int32_t this_gain = get_compression_gain(&buf->format, *in_buf[ch]);
if (this_gain < sample_gain)
sample_gain = this_gain;
}
@@ -345,7 +363,7 @@ void compressor_process(int count, struct dsp_data *data, int32_t *buf[])
output buffer sample pair/mono sample */
if (total_gain != UNITY)
{
- for (ch = 0; ch < num_chan; ch++)
+ for (int ch = 0; ch < num_chan; ch++)
{
*in_buf[ch] = FRACMUL_SHL(total_gain, *in_buf[ch], 7);
}
@@ -353,9 +371,33 @@ void compressor_process(int count, struct dsp_data *data, int32_t *buf[])
in_buf[0]++;
in_buf[1]++;
}
+
+ (void)this;
}
-void compressor_reset(void)
+/* DSP message hook */
+static intptr_t compressor_configure(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned int setting,
+ intptr_t value)
{
- release_gain = UNITY;
+ switch (setting)
+ {
+ case DSP_PROC_INIT:
+ if (value != 0)
+ break; /* Already enabled */
+ this->process[0] = compressor_process;
+ case DSP_RESET:
+ case DSP_FLUSH:
+ release_gain = UNITY;
+ break;
+ }
+
+ return 1;
+ (void)dsp;
}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(
+ COMPRESSOR,
+ compressor_configure);
diff --git a/lib/rbcodec/dsp/compressor.h b/lib/rbcodec/dsp/compressor.h
index d0e33f6e2c..e41950926e 100644
--- a/lib/rbcodec/dsp/compressor.h
+++ b/lib/rbcodec/dsp/compressor.h
@@ -18,12 +18,18 @@
* KIND, either express or implied.
*
****************************************************************************/
-
#ifndef COMPRESSOR_H
#define COMPRESSOR_H
-void compressor_process(int count, struct dsp_data *data, int32_t *buf[]);
-bool compressor_update(const struct compressor_settings *settings);
-void compressor_reset(void);
+struct compressor_settings
+{
+ int threshold;
+ int makeup_gain;
+ int ratio;
+ int knee;
+ int release_time;
+};
+
+void dsp_set_compressor(const struct compressor_settings *settings);
#endif /* COMPRESSOR_H */
diff --git a/lib/rbcodec/dsp/crossfeed.c b/lib/rbcodec/dsp/crossfeed.c
new file mode 100644
index 0000000000..ecb55644ee
--- /dev/null
+++ b/lib/rbcodec/dsp/crossfeed.c
@@ -0,0 +1,214 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "dsp.h"
+#include "dsp_filter.h"
+#include "fixedpoint.h"
+#include "fracmul.h"
+#include "replaygain.h"
+#include <string.h>
+#include "dsp_proc_entry.h"
+
+/* Implemented here or in target assembly code */
+void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p);
+
+/**
+ * Applies crossfeed to the stereo signal.
+ *
+ * Crossfeed is a process where listening over speakers is simulated. This
+ * is good for old hard panned stereo records, which might be quite fatiguing
+ * to listen to on headphones with no crossfeed.
+ */
+
+/* Crossfeed */
+static struct crossfeed_state
+{
+ int32_t gain; /* 00h: Direct path gain */
+ int32_t coefs[3]; /* 04h: Coefficients for the shelving filter */
+ int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */
+ int32_t delay[13*2]; /* 20h: Delay line buffer (L + R interleaved) */
+ int32_t *index; /* 88h: Current pointer into the delay line */
+ struct dsp_config *dsp; /* 8ch: Current DSP */
+ /* 90h */
+} crossfeed_state IBSS_ATTR;
+
+/* Discard the sample histories */
+static void crossfeed_flush(struct dsp_proc_entry *this)
+{
+ struct crossfeed_state *state = (void *)this->data;
+ memset(state->history, 0, sizeof (state->history));
+ memset(state->delay, 0, sizeof (state->delay));
+ state->index = state->delay;
+}
+
+
+/** DSP interface **/
+
+/* Crossfeed boot/format change function */
+static void crossfeed_process_new_format(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ struct crossfeed_state *state = (void *)this->data;
+ struct dsp_buffer *buf = *buf_p;
+
+ DSP_PRINT_FORMAT(DSP_PROC_CROSSFEED, DSP_PROC_CROSSFEED, buf->format);
+
+ bool active = buf->format.num_channels >= 2;
+ dsp_proc_activate(state->dsp, DSP_PROC_CROSSFEED, active);
+
+ if (!active)
+ {
+ /* Can't do this. Sleep until next change */
+ crossfeed_flush(this);
+ DEBUGF(" DSP_PROC_CROSSFEED- deactivated\n");
+ return;
+ }
+
+ /* Switch to the real function and call it once */
+ this->process[0] = crossfeed_process;
+ dsp_proc_call(this, buf_p, (unsigned)buf->format.changed - 1);
+}
+
+/* Enable or disable the crossfeed */
+void dsp_crossfeed_enable(bool enable)
+{
+ if (enable != !crossfeed_state.dsp)
+ return;
+
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ dsp_proc_enable(dsp, DSP_PROC_CROSSFEED, enable);
+}
+
+/* Set the gain of the dry mix */
+void dsp_set_crossfeed_direct_gain(int gain)
+{
+ uint32_t gain32 = get_replaygain_int(gain * 10);
+ crossfeed_state.gain =
+ gain32 >= (0x80000000ul >> 7) ? 0x7ffffffful: (gain32 << 7);
+}
+
+/* Both gains should be below 0 dB */
+void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
+{
+ int32_t *c = crossfeed_state.coefs;
+ long scaler = get_replaygain_int(lf_gain * 10) << 7;
+
+ cutoff = 0xffffffff / NATIVE_FREQUENCY * cutoff;
+ hf_gain -= lf_gain;
+ /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB
+ * point instead of shelf midpoint. This is for compatibility with the old
+ * crossfeed shelf filter and should be removed if crossfeed settings are
+ * ever made incompatible for any other good reason.
+ */
+ cutoff = fp_div(cutoff, get_replaygain_int(hf_gain*5), 24);
+ filter_shelf_coefs(cutoff, hf_gain, false, c);
+ /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains
+ * over 1 and can do this safely
+ */
+ c[0] = FRACMUL_SHL(c[0], scaler, 4);
+ c[1] = FRACMUL_SHL(c[1], scaler, 4);
+ c[2] <<= 4;
+}
+
+#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
+/* Apply the crossfade to the buffer in place */
+void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
+{
+ struct crossfeed_state *state = (void *)this->data;
+ struct dsp_buffer *buf = *buf_p;
+
+ int32_t *hist_l = &state->history[0];
+ int32_t *hist_r = &state->history[2];
+ int32_t *delay = state->delay;
+ int32_t *coefs = &state->coefs[0];
+ int32_t gain = state->gain;
+ int32_t *di = state->index;
+
+ int count = buf->remcount;
+
+ for (int i = 0; i < count; i++)
+ {
+ int32_t left = buf->p32[0][i];
+ int32_t right = buf->p32[1][i];
+
+ /* Filter delayed sample from left speaker */
+ int32_t acc = FRACMUL(*di, coefs[0]);
+ acc += FRACMUL(hist_l[0], coefs[1]);
+ acc += FRACMUL(hist_l[1], coefs[2]);
+ /* Save filter history for left speaker */
+ hist_l[1] = acc;
+ hist_l[0] = *di;
+ *di++ = left;
+ /* Filter delayed sample from right speaker */
+ acc = FRACMUL(*di, coefs[0]);
+ acc += FRACMUL(hist_r[0], coefs[1]);
+ acc += FRACMUL(hist_r[1], coefs[2]);
+ /* Save filter history for right speaker */
+ hist_r[1] = acc;
+ hist_r[0] = *di;
+ *di++ = right;
+ /* Now add the attenuated direct sound and write to outputs */
+ buf->p32[0][i] = FRACMUL(left, gain) + hist_r[1];
+ buf->p32[1][i] = FRACMUL(right, gain) + hist_l[1];
+
+ /* Wrap delay line index if bigger than delay line size */
+ if (di >= delay + 13*2)
+ di = delay;
+ }
+
+ /* Write back local copies of data we've modified */
+ state->index = di;
+}
+#endif /* CPU */
+
+/* DSP message hook */
+static intptr_t crossfeed_configure(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned int setting,
+ intptr_t value)
+{
+ switch (setting)
+ {
+ case DSP_PROC_INIT:
+ this->data = (intptr_t)&crossfeed_state;
+ this->process[0] = crossfeed_process_new_format;
+ this->process[1] = crossfeed_process_new_format;
+ ((struct crossfeed_state *)this->data)->dsp = dsp;
+ dsp_proc_activate(dsp, DSP_PROC_CROSSFEED, true);
+ case DSP_FLUSH:
+ crossfeed_flush(this);
+ break;
+
+ case DSP_PROC_CLOSE:
+ ((struct crossfeed_state *)this->data)->dsp = NULL;
+ break;
+ }
+
+ return 1;
+ (void)value;
+}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(
+ CROSSFEED,
+ crossfeed_configure);
diff --git a/lib/rbcodec/dsp/crossfeed.h b/lib/rbcodec/dsp/crossfeed.h
new file mode 100644
index 0000000000..63261bde9f
--- /dev/null
+++ b/lib/rbcodec/dsp/crossfeed.h
@@ -0,0 +1,28 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Thom Johansen
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef CROSSFEED_H
+#define CROSSFEED_H
+
+void dsp_crossfeed_enable(bool enable);
+void dsp_set_crossfeed_direct_gain(int gain);
+void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff);
+
+#endif /* CROSSFEED_H */
diff --git a/lib/rbcodec/dsp/dsp.c b/lib/rbcodec/dsp/dsp.c
deleted file mode 100644
index de647dc0dd..0000000000
--- a/lib/rbcodec/dsp/dsp.c
+++ /dev/null
@@ -1,1568 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2005 Miika Pekkarinen
- *
- * 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 "dsp.h"
-#include "dsp-util.h"
-#include "eq.h"
-#include "compressor.h"
-#include "kernel.h"
-#include "settings.h"
-#include "replaygain.h"
-#include "tdspeed.h"
-#include "core_alloc.h"
-#include "fixedpoint.h"
-#include "fracmul.h"
-
-/* Define LOGF_ENABLE to enable logf output in this file */
-/*#define LOGF_ENABLE*/
-#include "logf.h"
-
-/* 16-bit samples are scaled based on these constants. The shift should be
- * no more than 15.
- */
-#define WORD_SHIFT 12
-#define WORD_FRACBITS 27
-
-#define NATIVE_DEPTH 16
-#define SMALL_SAMPLE_BUF_COUNT 128 /* Per channel */
-#define DEFAULT_GAIN 0x01000000
-
-/* enums to index conversion properly with stereo mode and other settings */
-enum
-{
- SAMPLE_INPUT_LE_NATIVE_I_STEREO = STEREO_INTERLEAVED,
- SAMPLE_INPUT_LE_NATIVE_NI_STEREO = STEREO_NONINTERLEAVED,
- SAMPLE_INPUT_LE_NATIVE_MONO = STEREO_MONO,
- SAMPLE_INPUT_GT_NATIVE_I_STEREO = STEREO_INTERLEAVED + STEREO_NUM_MODES,
- SAMPLE_INPUT_GT_NATIVE_NI_STEREO = STEREO_NONINTERLEAVED + STEREO_NUM_MODES,
- SAMPLE_INPUT_GT_NATIVE_MONO = STEREO_MONO + STEREO_NUM_MODES,
- SAMPLE_INPUT_GT_NATIVE_1ST_INDEX = STEREO_NUM_MODES
-};
-
-enum
-{
- SAMPLE_OUTPUT_MONO = 0,
- SAMPLE_OUTPUT_STEREO,
- SAMPLE_OUTPUT_DITHERED_MONO,
- SAMPLE_OUTPUT_DITHERED_STEREO
-};
-
-/* No asm...yet */
-struct dither_data
-{
- long error[3]; /* 00h */
- long random; /* 0ch */
- /* 10h */
-};
-
-struct crossfeed_data
-{
- int32_t gain; /* 00h - Direct path gain */
- int32_t coefs[3]; /* 04h - Coefficients for the shelving filter */
- int32_t history[4]; /* 10h - Format is x[n - 1], y[n - 1] for both channels */
- int32_t delay[13][2]; /* 20h */
- int32_t *index; /* 88h - Current pointer into the delay line */
- /* 8ch */
-};
-
-/* Current setup is one lowshelf filters three peaking filters and one
- * highshelf filter. Varying the number of shelving filters make no sense,
- * but adding peaking filters is possible.
- */
-struct eq_state
-{
- char enabled[5]; /* 00h - Flags for active filters */
- struct eqfilter filters[5]; /* 08h - packing is 4? */
- /* 10ch */
-};
-
-/* Include header with defines which functions are implemented in assembly
- code for the target */
-#include <dsp_asm.h>
-
-/* Typedefs keep things much neater in this case */
-typedef void (*sample_input_fn_type)(int count, const char *src[],
- int32_t *dst[]);
-typedef int (*resample_fn_type)(int count, struct dsp_data *data,
- const int32_t *src[], int32_t *dst[]);
-typedef void (*sample_output_fn_type)(int count, struct dsp_data *data,
- const int32_t *src[], int16_t *dst);
-
-/* Single-DSP channel processing in place */
-typedef void (*channels_process_fn_type)(int count, int32_t *buf[]);
-/* DSP local channel processing in place */
-typedef void (*channels_process_dsp_fn_type)(int count, struct dsp_data *data,
- int32_t *buf[]);
-
-/*
- ***************************************************************************/
-
-struct dsp_config
-{
- struct dsp_data data; /* Config members for use in external routines */
- long codec_frequency; /* Sample rate of data coming from the codec */
- long frequency; /* Effective sample rate after pitch shift (if any) */
- int sample_depth;
- int sample_bytes;
- int stereo_mode;
- int32_t tdspeed_percent; /* Speed% * PITCH_SPEED_PRECISION */
-#ifdef HAVE_PITCHSCREEN
- bool tdspeed_active; /* Timestretch is in use */
-#endif
-#ifdef HAVE_SW_TONE_CONTROLS
- /* Filter struct for software bass/treble controls */
- struct eqfilter tone_filter;
-#endif
- /* Functions that change depending upon settings - NULL if stage is
- disabled */
- sample_input_fn_type input_samples;
- resample_fn_type resample;
- sample_output_fn_type output_samples;
- /* These will be NULL for the voice codec and is more economical that
- way */
- channels_process_dsp_fn_type apply_gain;
- channels_process_fn_type apply_crossfeed;
- channels_process_fn_type eq_process;
- channels_process_fn_type channels_process;
- channels_process_dsp_fn_type compressor_process;
-};
-
-/* General DSP config */
-static struct dsp_config dsp_conf[2] IBSS_ATTR; /* 0=A, 1=V */
-/* Dithering */
-static struct dither_data dither_data[2] IBSS_ATTR; /* 0=left, 1=right */
-static long dither_mask IBSS_ATTR;
-static long dither_bias IBSS_ATTR;
-/* Crossfeed */
-struct crossfeed_data crossfeed_data IDATA_ATTR = /* A */
-{
- .index = (int32_t *)crossfeed_data.delay
-};
-
-/* Equalizer */
-static struct eq_state eq_data; /* A */
-
-/* Software tone controls */
-#ifdef HAVE_SW_TONE_CONTROLS
-static int prescale; /* A/V */
-static int bass; /* A/V */
-static int treble; /* A/V */
-#endif
-
-/* Settings applicable to audio codec only */
-#ifdef HAVE_PITCHSCREEN
-static int32_t pitch_ratio = PITCH_SPEED_100;
-static int big_sample_locks;
-#endif
-static int channels_mode;
- long dsp_sw_gain;
- long dsp_sw_cross;
-static bool dither_enabled;
-static long eq_precut;
-static long track_gain;
-static bool new_gain;
-static long album_gain;
-static long track_peak;
-static long album_peak;
-static long replaygain;
-static bool crossfeed_enabled;
-
-#define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO])
-#define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE])
-
-/* The internal format is 32-bit samples, non-interleaved, stereo. This
- * format is similar to the raw output from several codecs, so the amount
- * of copying needed is minimized for that case.
- */
-
-#define RESAMPLE_RATIO 4 /* Enough for 11,025 Hz -> 44,100 Hz */
-#define SMALL_RESAMPLE_BUF_COUNT (SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO)
-#define BIG_SAMPLE_BUF_COUNT SMALL_RESAMPLE_BUF_COUNT
-#define BIG_RESAMPLE_BUF_COUNT (BIG_SAMPLE_BUF_COUNT * RESAMPLE_RATIO)
-
-static int32_t small_sample_buf[2][SMALL_SAMPLE_BUF_COUNT] IBSS_ATTR;
-static int32_t small_resample_buf[2][SMALL_RESAMPLE_BUF_COUNT] IBSS_ATTR;
-
-#ifdef HAVE_PITCHSCREEN
-static int32_t (* big_sample_buf)[BIG_SAMPLE_BUF_COUNT] = NULL;
-static int32_t (* big_resample_buf)[BIG_RESAMPLE_BUF_COUNT] = NULL;
-#endif
-
-static int sample_buf_count = SMALL_SAMPLE_BUF_COUNT;
-static int32_t *sample_buf[2] = { small_sample_buf[0], small_sample_buf[1] };
-static int resample_buf_count = SMALL_RESAMPLE_BUF_COUNT;
-static int32_t *resample_buf[2] = { small_resample_buf[0], small_resample_buf[1] };
-
-#ifdef HAVE_PITCHSCREEN
-int32_t sound_get_pitch(void)
-{
- return pitch_ratio;
-}
-
-void sound_set_pitch(int32_t percent)
-{
- pitch_ratio = percent;
- dsp_configure(&AUDIO_DSP, DSP_SWITCH_FREQUENCY,
- AUDIO_DSP.codec_frequency);
-}
-
-static void tdspeed_set_pointers( bool time_stretch_active )
-{
- if( time_stretch_active )
- {
- sample_buf_count = BIG_SAMPLE_BUF_COUNT;
- resample_buf_count = BIG_RESAMPLE_BUF_COUNT;
- sample_buf[0] = big_sample_buf[0];
- sample_buf[1] = big_sample_buf[1];
- resample_buf[0] = big_resample_buf[0];
- resample_buf[1] = big_resample_buf[1];
- }
- else
- {
- sample_buf_count = SMALL_SAMPLE_BUF_COUNT;
- resample_buf_count = SMALL_RESAMPLE_BUF_COUNT;
- sample_buf[0] = small_sample_buf[0];
- sample_buf[1] = small_sample_buf[1];
- resample_buf[0] = small_resample_buf[0];
- resample_buf[1] = small_resample_buf[1];
- }
-}
-
-static void tdspeed_setup(struct dsp_config *dspc)
-{
- /* Assume timestretch will not be used */
- dspc->tdspeed_active = false;
-
- tdspeed_set_pointers( false );
-
- if (!dsp_timestretch_available())
- return; /* Timestretch not enabled or buffer not allocated */
-
- if (dspc->tdspeed_percent == 0)
- dspc->tdspeed_percent = PITCH_SPEED_100;
-
- if (!tdspeed_config(
- dspc->codec_frequency == 0 ? NATIVE_FREQUENCY : dspc->codec_frequency,
- dspc->stereo_mode != STEREO_MONO,
- dspc->tdspeed_percent))
- return; /* Timestretch not possible or needed with these parameters */
-
- /* Timestretch is to be used */
- dspc->tdspeed_active = true;
-
- tdspeed_set_pointers( true );
-}
-
-
-static int move_callback(int handle, void* current, void* new)
-{
- (void)handle;(void)current;
-
- if ( big_sample_locks > 0 )
- return BUFLIB_CB_CANNOT_MOVE;
-
- big_sample_buf = new;
-
- /* no allocation without timestretch enabled */
- tdspeed_set_pointers( true );
- return BUFLIB_CB_OK;
-}
-
-static void lock_sample_buf( bool lock )
-{
- if ( lock )
- big_sample_locks++;
- else
- big_sample_locks--;
-}
-
-static struct buflib_callbacks ops = {
- .move_callback = move_callback,
- .shrink_callback = NULL,
-};
-
-
-void dsp_timestretch_enable(bool enabled)
-{
- /* Hook to set up timestretch buffer on first call to settings_apply() */
- static int handle = -1;
- if (enabled)
- {
- if (big_sample_buf)
- return; /* already allocated and enabled */
-
- /* Set up timestretch buffers */
- big_sample_buf = &small_resample_buf[0];
- handle = core_alloc_ex("resample buf",
- 2 * BIG_RESAMPLE_BUF_COUNT * sizeof(int32_t),
- &ops);
- big_sample_locks = 0;
- enabled = handle >= 0;
-
- if (enabled)
- {
- /* success, now setup tdspeed */
- big_resample_buf = core_get_data(handle);
-
- tdspeed_init();
- tdspeed_setup(&AUDIO_DSP);
- }
- }
-
- if (!enabled)
- {
- dsp_set_timestretch(PITCH_SPEED_100);
- tdspeed_finish();
-
- if (handle >= 0)
- core_free(handle);
-
- handle = -1;
- big_sample_buf = NULL;
- }
-}
-
-void dsp_set_timestretch(int32_t percent)
-{
- AUDIO_DSP.tdspeed_percent = percent;
- tdspeed_setup(&AUDIO_DSP);
-}
-
-int32_t dsp_get_timestretch()
-{
- return AUDIO_DSP.tdspeed_percent;
-}
-
-bool dsp_timestretch_available()
-{
- return (global_settings.timestretch_enabled && big_sample_buf);
-}
-#endif /* HAVE_PITCHSCREEN */
-
-/* Convert count samples to the internal format, if needed. Updates src
- * to point past the samples "consumed" and dst is set to point to the
- * samples to consume. Note that for mono, dst[0] equals dst[1], as there
- * is no point in processing the same data twice.
- */
-
-/* convert count 16-bit mono to 32-bit mono */
-static void sample_input_lte_native_mono(
- int count, const char *src[], int32_t *dst[])
-{
- const int16_t *s = (int16_t *) src[0];
- const int16_t * const send = s + count;
- int32_t *d = dst[0] = dst[1] = sample_buf[0];
- int scale = WORD_SHIFT;
-
- while (s < send)
- {
- *d++ = *s++ << scale;
- }
-
- src[0] = (char *)s;
-}
-
-/* convert count 16-bit interleaved stereo to 32-bit noninterleaved */
-static void sample_input_lte_native_i_stereo(
- int count, const char *src[], int32_t *dst[])
-{
- const int32_t *s = (int32_t *) src[0];
- const int32_t * const send = s + count;
- int32_t *dl = dst[0] = sample_buf[0];
- int32_t *dr = dst[1] = sample_buf[1];
- int scale = WORD_SHIFT;
-
- while (s < send)
- {
- int32_t slr = *s++;
-#ifdef ROCKBOX_LITTLE_ENDIAN
- *dl++ = (slr >> 16) << scale;
- *dr++ = (int32_t)(int16_t)slr << scale;
-#else /* ROCKBOX_BIG_ENDIAN */
- *dl++ = (int32_t)(int16_t)slr << scale;
- *dr++ = (slr >> 16) << scale;
-#endif
- }
-
- src[0] = (char *)s;
-}
-
-/* convert count 16-bit noninterleaved stereo to 32-bit noninterleaved */
-static void sample_input_lte_native_ni_stereo(
- int count, const char *src[], int32_t *dst[])
-{
- const int16_t *sl = (int16_t *) src[0];
- const int16_t *sr = (int16_t *) src[1];
- const int16_t * const slend = sl + count;
- int32_t *dl = dst[0] = sample_buf[0];
- int32_t *dr = dst[1] = sample_buf[1];
- int scale = WORD_SHIFT;
-
- while (sl < slend)
- {
- *dl++ = *sl++ << scale;
- *dr++ = *sr++ << scale;
- }
-
- src[0] = (char *)sl;
- src[1] = (char *)sr;
-}
-
-/* convert count 32-bit mono to 32-bit mono */
-static void sample_input_gt_native_mono(
- int count, const char *src[], int32_t *dst[])
-{
- dst[0] = dst[1] = (int32_t *)src[0];
- src[0] = (char *)(dst[0] + count);
-}
-
-/* convert count 32-bit interleaved stereo to 32-bit noninterleaved stereo */
-static void sample_input_gt_native_i_stereo(
- int count, const char *src[], int32_t *dst[])
-{
- const int32_t *s = (int32_t *)src[0];
- const int32_t * const send = s + 2*count;
- int32_t *dl = dst[0] = sample_buf[0];
- int32_t *dr = dst[1] = sample_buf[1];
-
- while (s < send)
- {
- *dl++ = *s++;
- *dr++ = *s++;
- }
-
- src[0] = (char *)send;
-}
-
-/* convert 32 bit-noninterleaved stereo to 32-bit noninterleaved stereo */
-static void sample_input_gt_native_ni_stereo(
- int count, const char *src[], int32_t *dst[])
-{
- dst[0] = (int32_t *)src[0];
- dst[1] = (int32_t *)src[1];
- src[0] = (char *)(dst[0] + count);
- src[1] = (char *)(dst[1] + count);
-}
-
-/**
- * sample_input_new_format()
- *
- * set the to-native sample conversion function based on dsp sample parameters
- *
- * !DSPPARAMSYNC
- * needs syncing with changes to the following dsp parameters:
- * * dsp->stereo_mode (A/V)
- * * dsp->sample_depth (A/V)
- */
-static void sample_input_new_format(struct dsp_config *dsp)
-{
- static const sample_input_fn_type sample_input_functions[] =
- {
- [SAMPLE_INPUT_LE_NATIVE_I_STEREO] = sample_input_lte_native_i_stereo,
- [SAMPLE_INPUT_LE_NATIVE_NI_STEREO] = sample_input_lte_native_ni_stereo,
- [SAMPLE_INPUT_LE_NATIVE_MONO] = sample_input_lte_native_mono,
- [SAMPLE_INPUT_GT_NATIVE_I_STEREO] = sample_input_gt_native_i_stereo,
- [SAMPLE_INPUT_GT_NATIVE_NI_STEREO] = sample_input_gt_native_ni_stereo,
- [SAMPLE_INPUT_GT_NATIVE_MONO] = sample_input_gt_native_mono,
- };
-
- int convert = dsp->stereo_mode;
-
- if (dsp->sample_depth > NATIVE_DEPTH)
- convert += SAMPLE_INPUT_GT_NATIVE_1ST_INDEX;
-
- dsp->input_samples = sample_input_functions[convert];
-}
-
-
-#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
-/* write mono internal format to output format */
-static void sample_output_mono(int count, struct dsp_data *data,
- const int32_t *src[], int16_t *dst)
-{
- const int32_t *s0 = src[0];
- const int scale = data->output_scale;
- const int dc_bias = 1 << (scale - 1);
-
- while (count-- > 0)
- {
- int32_t lr = clip_sample_16((*s0++ + dc_bias) >> scale);
- *dst++ = lr;
- *dst++ = lr;
- }
-}
-#endif /* DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO */
-
-/* write stereo internal format to output format */
-#ifndef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
-static void sample_output_stereo(int count, struct dsp_data *data,
- const int32_t *src[], int16_t *dst)
-{
- const int32_t *s0 = src[0];
- const int32_t *s1 = src[1];
- const int scale = data->output_scale;
- const int dc_bias = 1 << (scale - 1);
-
- while (count-- > 0)
- {
- *dst++ = clip_sample_16((*s0++ + dc_bias) >> scale);
- *dst++ = clip_sample_16((*s1++ + dc_bias) >> scale);
- }
-}
-#endif /* DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO */
-
-/**
- * The "dither" code to convert the 24-bit samples produced by libmad was
- * taken from the coolplayer project - coolplayer.sourceforge.net
- *
- * This function handles mono and stereo outputs.
- */
-static void sample_output_dithered(int count, struct dsp_data *data,
- const int32_t *src[], int16_t *dst)
-{
- const int32_t mask = dither_mask;
- const int32_t bias = dither_bias;
- const int scale = data->output_scale;
- const int32_t min = data->clip_min;
- const int32_t max = data->clip_max;
- const int32_t range = max - min;
- int ch;
- int16_t *d;
-
- for (ch = 0; ch < data->num_channels; ch++)
- {
- struct dither_data * const dither = &dither_data[ch];
- const int32_t *s = src[ch];
- int i;
-
- for (i = 0, d = &dst[ch]; i < count; i++, s++, d += 2)
- {
- int32_t output, sample;
- int32_t random;
-
- /* Noise shape and bias (for correct rounding later) */
- sample = *s;
- sample += dither->error[0] - dither->error[1] + dither->error[2];
- dither->error[2] = dither->error[1];
- dither->error[1] = dither->error[0]/2;
-
- output = sample + bias;
-
- /* Dither, highpass triangle PDF */
- random = dither->random*0x0019660dL + 0x3c6ef35fL;
- output += (random & mask) - (dither->random & mask);
- dither->random = random;
-
- /* Round sample to output range */
- output &= ~mask;
-
- /* Error feedback */
- dither->error[0] = sample - output;
-
- /* Clip */
- if ((uint32_t)(output - min) > (uint32_t)range)
- {
- int32_t c = min;
- if (output > min)
- c += range;
- output = c;
- }
-
- /* Quantize and store */
- *d = output >> scale;
- }
- }
-
- if (data->num_channels == 2)
- return;
-
- /* Have to duplicate left samples into the right channel since
- pcm buffer and hardware is interleaved stereo */
- d = &dst[0];
-
- while (count-- > 0)
- {
- int16_t s = *d++;
- *d++ = s;
- }
-}
-
-/**
- * sample_output_new_format()
- *
- * set the from-native to ouput sample conversion routine
- *
- * !DSPPARAMSYNC
- * needs syncing with changes to the following dsp parameters:
- * * dsp->stereo_mode (A/V)
- * * dither_enabled (A)
- */
-static void sample_output_new_format(struct dsp_config *dsp)
-{
- static const sample_output_fn_type sample_output_functions[] =
- {
- sample_output_mono,
- sample_output_stereo,
- sample_output_dithered,
- sample_output_dithered
- };
-
- int out = dsp->data.num_channels - 1;
-
- if (dsp == &AUDIO_DSP && dither_enabled)
- out += 2;
-
- dsp->output_samples = sample_output_functions[out];
-}
-
-/**
- * Linear interpolation resampling that introduces a one sample delay because
- * of our inability to look into the future at the end of a frame.
- */
-#ifndef DSP_HAVE_ASM_RESAMPLING
-static int dsp_downsample(int count, struct dsp_data *data,
- const int32_t *src[], int32_t *dst[])
-{
- int ch = data->num_channels - 1;
- uint32_t delta = data->resample_data.delta;
- uint32_t phase, pos;
- int32_t *d;
-
- /* Rolled channel loop actually showed slightly faster. */
- do
- {
- /* Just initialize things and not worry too much about the relatively
- * uncommon case of not being able to spit out a sample for the frame.
- */
- const int32_t *s = src[ch];
- int32_t last = data->resample_data.last_sample[ch];
-
- data->resample_data.last_sample[ch] = s[count - 1];
- d = dst[ch];
- phase = data->resample_data.phase;
- pos = phase >> 16;
-
- /* Do we need last sample of previous frame for interpolation? */
- if (pos > 0)
- last = s[pos - 1];
-
- while (pos < (uint32_t)count)
- {
- *d++ = last + FRACMUL((phase & 0xffff) << 15, s[pos] - last);
- phase += delta;
- pos = phase >> 16;
- last = s[pos - 1];
- }
- }
- while (--ch >= 0);
-
- /* Wrap phase accumulator back to start of next frame. */
- data->resample_data.phase = phase - (count << 16);
- return d - dst[0];
-}
-
-static int dsp_upsample(int count, struct dsp_data *data,
- const int32_t *src[], int32_t *dst[])
-{
- int ch = data->num_channels - 1;
- uint32_t delta = data->resample_data.delta;
- uint32_t phase, pos;
- int32_t *d;
-
- /* Rolled channel loop actually showed slightly faster. */
- do
- {
- /* Should always be able to output a sample for a ratio up to RESAMPLE_RATIO */
- const int32_t *s = src[ch];
- int32_t last = data->resample_data.last_sample[ch];
-
- data->resample_data.last_sample[ch] = s[count - 1];
- d = dst[ch];
- phase = data->resample_data.phase;
- pos = phase >> 16;
-
- while (pos == 0)
- {
- *d++ = last + FRACMUL((phase & 0xffff) << 15, s[0] - last);
- phase += delta;
- pos = phase >> 16;
- }
-
- while (pos < (uint32_t)count)
- {
- last = s[pos - 1];
- *d++ = last + FRACMUL((phase & 0xffff) << 15, s[pos] - last);
- phase += delta;
- pos = phase >> 16;
- }
- }
- while (--ch >= 0);
-
- /* Wrap phase accumulator back to start of next frame. */
- data->resample_data.phase = phase & 0xffff;
- return d - dst[0];
-}
-#endif /* DSP_HAVE_ASM_RESAMPLING */
-
-static void resampler_new_delta(struct dsp_config *dsp)
-{
- dsp->data.resample_data.delta = (unsigned long)
- dsp->frequency * 65536LL / NATIVE_FREQUENCY;
-
- if (dsp->frequency == NATIVE_FREQUENCY)
- {
- /* NOTE: If fully glitch-free transistions from no resampling to
- resampling are desired, last_sample history should be maintained
- even when not resampling. */
- dsp->resample = NULL;
- dsp->data.resample_data.phase = 0;
- dsp->data.resample_data.last_sample[0] = 0;
- dsp->data.resample_data.last_sample[1] = 0;
- }
- else if (dsp->frequency < NATIVE_FREQUENCY)
- dsp->resample = dsp_upsample;
- else
- dsp->resample = dsp_downsample;
-}
-
-/* Resample count stereo samples. Updates the src array, if resampling is
- * done, to refer to the resampled data. Returns number of stereo samples
- * for further processing.
- */
-static inline int resample(struct dsp_config *dsp, int count, int32_t *src[])
-{
- int32_t *dst[2] =
- {
- resample_buf[0],
- resample_buf[1]
- };
- lock_sample_buf( true );
- count = dsp->resample(count, &dsp->data, (const int32_t **)src, dst);
-
- src[0] = dst[0];
- src[1] = dst[dsp->data.num_channels - 1];
- lock_sample_buf( false );
- return count;
-}
-
-static void dither_init(struct dsp_config *dsp)
-{
- memset(dither_data, 0, sizeof (dither_data));
- dither_bias = (1L << (dsp->data.frac_bits - NATIVE_DEPTH));
- dither_mask = (1L << (dsp->data.frac_bits + 1 - NATIVE_DEPTH)) - 1;
-}
-
-void dsp_dither_enable(bool enable)
-{
- struct dsp_config *dsp = &AUDIO_DSP;
- dither_enabled = enable;
- sample_output_new_format(dsp);
-}
-
-/* Applies crossfeed to the stereo signal in src.
- * Crossfeed is a process where listening over speakers is simulated. This
- * is good for old hard panned stereo records, which might be quite fatiguing
- * to listen to on headphones with no crossfeed.
- */
-#ifndef DSP_HAVE_ASM_CROSSFEED
-static void apply_crossfeed(int count, int32_t *buf[])
-{
- int32_t *hist_l = &crossfeed_data.history[0];
- int32_t *hist_r = &crossfeed_data.history[2];
- int32_t *delay = &crossfeed_data.delay[0][0];
- int32_t *coefs = &crossfeed_data.coefs[0];
- int32_t gain = crossfeed_data.gain;
- int32_t *di = crossfeed_data.index;
-
- int32_t acc;
- int32_t left, right;
- int i;
-
- for (i = 0; i < count; i++)
- {
- left = buf[0][i];
- right = buf[1][i];
-
- /* Filter delayed sample from left speaker */
- acc = FRACMUL(*di, coefs[0]);
- acc += FRACMUL(hist_l[0], coefs[1]);
- acc += FRACMUL(hist_l[1], coefs[2]);
- /* Save filter history for left speaker */
- hist_l[1] = acc;
- hist_l[0] = *di;
- *di++ = left;
- /* Filter delayed sample from right speaker */
- acc = FRACMUL(*di, coefs[0]);
- acc += FRACMUL(hist_r[0], coefs[1]);
- acc += FRACMUL(hist_r[1], coefs[2]);
- /* Save filter history for right speaker */
- hist_r[1] = acc;
- hist_r[0] = *di;
- *di++ = right;
- /* Now add the attenuated direct sound and write to outputs */
- buf[0][i] = FRACMUL(left, gain) + hist_r[1];
- buf[1][i] = FRACMUL(right, gain) + hist_l[1];
-
- /* Wrap delay line index if bigger than delay line size */
- if (di >= delay + 13*2)
- di = delay;
- }
- /* Write back local copies of data we've modified */
- crossfeed_data.index = di;
-}
-#endif /* DSP_HAVE_ASM_CROSSFEED */
-
-/**
- * dsp_set_crossfeed(bool enable)
- *
- * !DSPPARAMSYNC
- * needs syncing with changes to the following dsp parameters:
- * * dsp->stereo_mode (A)
- */
-void dsp_set_crossfeed(bool enable)
-{
- crossfeed_enabled = enable;
- AUDIO_DSP.apply_crossfeed = (enable && AUDIO_DSP.data.num_channels > 1)
- ? apply_crossfeed : NULL;
-}
-
-void dsp_set_crossfeed_direct_gain(int gain)
-{
- crossfeed_data.gain = get_replaygain_int(gain * 10) << 7;
- /* If gain is negative, the calculation overflowed and we need to clamp */
- if (crossfeed_data.gain < 0)
- crossfeed_data.gain = 0x7fffffff;
-}
-
-/* Both gains should be below 0 dB */
-void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
-{
- int32_t *c = crossfeed_data.coefs;
- long scaler = get_replaygain_int(lf_gain * 10) << 7;
-
- cutoff = 0xffffffff/NATIVE_FREQUENCY*cutoff;
- hf_gain -= lf_gain;
- /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB
- * point instead of shelf midpoint. This is for compatibility with the old
- * crossfeed shelf filter and should be removed if crossfeed settings are
- * ever made incompatible for any other good reason.
- */
- cutoff = fp_div(cutoff, get_replaygain_int(hf_gain*5), 24);
- filter_shelf_coefs(cutoff, hf_gain, false, c);
- /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains
- * over 1 and can do this safely
- */
- c[0] = FRACMUL_SHL(c[0], scaler, 4);
- c[1] = FRACMUL_SHL(c[1], scaler, 4);
- c[2] <<= 4;
-}
-
-/* Apply a constant gain to the samples (e.g., for ReplayGain).
- * Note that this must be called before the resampler.
- */
-#ifndef DSP_HAVE_ASM_APPLY_GAIN
-static void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[])
-{
- const int32_t gain = data->gain;
- int ch;
-
- for (ch = 0; ch < data->num_channels; ch++)
- {
- int32_t *d = buf[ch];
- int i;
-
- for (i = 0; i < count; i++)
- d[i] = FRACMUL_SHL(d[i], gain, 8);
- }
-}
-#endif /* DSP_HAVE_ASM_APPLY_GAIN */
-
-/* Combine all gains to a global gain. */
-static void set_gain(struct dsp_config *dsp)
-{
- /* gains are in S7.24 format */
- dsp->data.gain = DEFAULT_GAIN;
-
- /* Replay gain not relevant to voice */
- if (dsp == &AUDIO_DSP && replaygain)
- {
- dsp->data.gain = replaygain;
- }
-
- if (dsp->eq_process && eq_precut)
- {
- dsp->data.gain = fp_mul(dsp->data.gain, eq_precut, 24);
- }
-
-#ifdef HAVE_SW_VOLUME_CONTROL
- if (global_settings.volume < SW_VOLUME_MAX ||
- global_settings.volume > SW_VOLUME_MIN)
- {
- int vol_gain = get_replaygain_int(global_settings.volume * 100);
- dsp->data.gain = (long) (((int64_t) dsp->data.gain * vol_gain) >> 24);
- }
-#endif
-
- if (dsp->data.gain == DEFAULT_GAIN)
- {
- dsp->data.gain = 0;
- }
- else
- {
- dsp->data.gain >>= 1; /* convert gain to S8.23 format */
- }
-
- dsp->apply_gain = dsp->data.gain != 0 ? dsp_apply_gain : NULL;
-}
-
-/**
- * Update the amount to cut the audio before applying the equalizer.
- *
- * @param precut to apply in decibels (multiplied by 10)
- */
-void dsp_set_eq_precut(int precut)
-{
- eq_precut = get_replaygain_int(precut * -10);
- set_gain(&AUDIO_DSP);
-}
-
-/**
- * Synchronize the equalizer filter coefficients with the global settings.
- *
- * @param band the equalizer band to synchronize
- */
-void dsp_set_eq_coefs(int band, int cutoff, int q, int gain)
-{
- /* Convert user settings to format required by coef generator functions */
- cutoff = 0xffffffff / NATIVE_FREQUENCY * cutoff;
-
- if (q == 0)
- q = 1;
-
- /* NOTE: The coef functions assume the EMAC unit is in fractional mode,
- which it should be, since we're executed from the main thread. */
-
- /* Assume a band is disabled if the gain is zero */
- if (gain == 0)
- {
- eq_data.enabled[band] = 0;
- }
- else
- {
- if (band == 0)
- eq_ls_coefs(cutoff, q, gain, eq_data.filters[band].coefs);
- else if (band == 4)
- eq_hs_coefs(cutoff, q, gain, eq_data.filters[band].coefs);
- else
- eq_pk_coefs(cutoff, q, gain, eq_data.filters[band].coefs);
-
- eq_data.enabled[band] = 1;
- }
-}
-
-/* Apply EQ filters to those bands that have got it switched on. */
-static void eq_process(int count, int32_t *buf[])
-{
- static const int shifts[] =
- {
- EQ_SHELF_SHIFT, /* low shelf */
- EQ_PEAK_SHIFT, /* peaking */
- EQ_PEAK_SHIFT, /* peaking */
- EQ_PEAK_SHIFT, /* peaking */
- EQ_SHELF_SHIFT, /* high shelf */
- };
- unsigned int channels = AUDIO_DSP.data.num_channels;
- int i;
-
- /* filter configuration currently is 1 low shelf filter, 3 band peaking
- filters and 1 high shelf filter, in that order. we need to know this
- so we can choose the correct shift factor.
- */
- for (i = 0; i < 5; i++)
- {
- if (!eq_data.enabled[i])
- continue;
- eq_filter(buf, &eq_data.filters[i], count, channels, shifts[i]);
- }
-}
-
-/**
- * Use to enable the equalizer.
- *
- * @param enable true to enable the equalizer
- */
-void dsp_set_eq(bool enable)
-{
- AUDIO_DSP.eq_process = enable ? eq_process : NULL;
- set_gain(&AUDIO_DSP);
-}
-
-static void dsp_set_stereo_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 = ((int64_t)(0x7fffff + width) << 22) / width;
- cross = straight - 0x7fffff;
- }
-
- dsp_sw_gain = straight << 8;
- dsp_sw_cross = cross << 8;
-}
-
-/**
- * Implements the different channel configurations and stereo width.
- */
-
-/* SOUND_CHAN_STEREO mode is a noop so has no function - just outline one for
- * completeness. */
-#if 0
-static void channels_process_sound_chan_stereo(int count, int32_t *buf[])
-{
- /* The channels are each just themselves */
- (void)count; (void)buf;
-}
-#endif
-
-#ifndef DSP_HAVE_ASM_SOUND_CHAN_MONO
-static void channels_process_sound_chan_mono(int count, int32_t *buf[])
-{
- int32_t *sl = buf[0], *sr = buf[1];
-
- while (count-- > 0)
- {
- int32_t lr = *sl/2 + *sr/2;
- *sl++ = lr;
- *sr++ = lr;
- }
-}
-#endif /* DSP_HAVE_ASM_SOUND_CHAN_MONO */
-
-#ifndef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
-static void channels_process_sound_chan_custom(int count, int32_t *buf[])
-{
- const int32_t gain = dsp_sw_gain;
- const int32_t cross = dsp_sw_cross;
- int32_t *sl = buf[0], *sr = buf[1];
-
- while (count-- > 0)
- {
- int32_t l = *sl;
- int32_t r = *sr;
- *sl++ = FRACMUL(l, gain) + FRACMUL(r, cross);
- *sr++ = FRACMUL(r, gain) + FRACMUL(l, cross);
- }
-}
-#endif /* DSP_HAVE_ASM_SOUND_CHAN_CUSTOM */
-
-static void channels_process_sound_chan_mono_left(int count, int32_t *buf[])
-{
- /* Just copy over the other channel */
- memcpy(buf[1], buf[0], count * sizeof (*buf));
-}
-
-static void channels_process_sound_chan_mono_right(int count, int32_t *buf[])
-{
- /* Just copy over the other channel */
- memcpy(buf[0], buf[1], count * sizeof (*buf));
-}
-
-#ifndef DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
-static void channels_process_sound_chan_karaoke(int count, int32_t *buf[])
-{
- int32_t *sl = buf[0], *sr = buf[1];
-
- while (count-- > 0)
- {
- int32_t ch = *sl/2 - *sr/2;
- *sl++ = ch;
- *sr++ = -ch;
- }
-}
-#endif /* DSP_HAVE_ASM_SOUND_CHAN_KARAOKE */
-
-static void dsp_set_channel_config(int value)
-{
- static const channels_process_fn_type channels_process_functions[] =
- {
- /* SOUND_CHAN_STEREO = All-purpose index for no channel processing */
- [SOUND_CHAN_STEREO] = NULL,
- [SOUND_CHAN_MONO] = channels_process_sound_chan_mono,
- [SOUND_CHAN_CUSTOM] = channels_process_sound_chan_custom,
- [SOUND_CHAN_MONO_LEFT] = channels_process_sound_chan_mono_left,
- [SOUND_CHAN_MONO_RIGHT] = channels_process_sound_chan_mono_right,
- [SOUND_CHAN_KARAOKE] = channels_process_sound_chan_karaoke,
- };
-
- if ((unsigned)value >= ARRAYLEN(channels_process_functions) ||
- AUDIO_DSP.stereo_mode == STEREO_MONO)
- {
- value = SOUND_CHAN_STEREO;
- }
-
- /* This doesn't apply to voice */
- channels_mode = value;
- AUDIO_DSP.channels_process = channels_process_functions[value];
-}
-
-#if CONFIG_CODEC == SWCODEC
-
-#ifdef HAVE_SW_TONE_CONTROLS
-static void set_tone_controls(void)
-{
- filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
- 0xffffffff/NATIVE_FREQUENCY*3500,
- bass, treble, -prescale,
- AUDIO_DSP.tone_filter.coefs);
- /* Sync the voice dsp coefficients */
- memcpy(&VOICE_DSP.tone_filter.coefs, AUDIO_DSP.tone_filter.coefs,
- sizeof (VOICE_DSP.tone_filter.coefs));
-}
-#endif
-
-/* Hook back from firmware/ part of audio, which can't/shouldn't call apps/
- * code directly.
- */
-int dsp_callback(int msg, intptr_t param)
-{
- switch (msg)
- {
-#ifdef HAVE_SW_TONE_CONTROLS
- case DSP_CALLBACK_SET_PRESCALE:
- prescale = param;
- set_tone_controls();
- break;
- /* prescaler is always set after calling any of these, so we wait with
- * calculating coefs until the above case is hit.
- */
- case DSP_CALLBACK_SET_BASS:
- bass = param;
- break;
- case DSP_CALLBACK_SET_TREBLE:
- treble = param;
- break;
-#ifdef HAVE_SW_VOLUME_CONTROL
- case DSP_CALLBACK_SET_SW_VOLUME:
- set_gain(&AUDIO_DSP);
- break;
-#endif
-#endif
- case DSP_CALLBACK_SET_CHANNEL_CONFIG:
- dsp_set_channel_config(param);
- break;
- case DSP_CALLBACK_SET_STEREO_WIDTH:
- dsp_set_stereo_width(param);
- break;
- default:
- break;
- }
- return 0;
-}
-#endif
-
-/* Process and convert src audio to dst based on the DSP configuration,
- * reading count number of audio samples. dst is assumed to be large
- * enough; use dsp_output_count() to get the required number. src is an
- * array of pointers; for mono and interleaved stereo, it contains one
- * pointer to the start of the audio data and the other is ignored; for
- * non-interleaved stereo, it contains two pointers, one for each audio
- * channel. Returns number of bytes written to dst.
- */
-int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
-{
- static int32_t *tmp[2]; /* tdspeed_doit() needs it static */
- static long last_yield;
- long tick;
- int written = 0;
-
-#if defined(CPU_COLDFIRE)
- /* set emac unit for dsp processing, and save old macsr, we're running in
- codec thread context at this point, so can't clobber it */
- unsigned long old_macsr = coldfire_get_macsr();
- coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE);
-#endif
-
- if (new_gain)
- dsp_set_replaygain(); /* Gain has changed */
-
- /* Perform at least one yield before starting */
- last_yield = current_tick;
- yield();
-
- /* Testing function pointers for NULL is preferred since the pointer
- will be preloaded to be used for the call if not. */
- while (count > 0)
- {
- int samples = MIN(sample_buf_count, count);
- count -= samples;
-
- dsp->input_samples(samples, src, tmp);
-
-#ifdef HAVE_PITCHSCREEN
- if (dsp->tdspeed_active)
- samples = tdspeed_doit(tmp, samples);
-#endif
-
- int chunk_offset = 0;
- while (samples > 0)
- {
- int32_t *t2[2];
- t2[0] = tmp[0]+chunk_offset;
- t2[1] = tmp[1]+chunk_offset;
-
- int chunk = MIN(sample_buf_count, samples);
- chunk_offset += chunk;
- samples -= chunk;
-
- if (dsp->apply_gain)
- dsp->apply_gain(chunk, &dsp->data, t2);
-
- if (dsp->resample && (chunk = resample(dsp, chunk, t2)) <= 0)
- break; /* I'm pretty sure we're downsampling here */
-
- if (dsp->apply_crossfeed)
- dsp->apply_crossfeed(chunk, t2);
-
- if (dsp->eq_process)
- dsp->eq_process(chunk, t2);
-
-#ifdef HAVE_SW_TONE_CONTROLS
- if ((bass | treble) != 0)
- eq_filter(t2, &dsp->tone_filter, chunk,
- dsp->data.num_channels, FILTER_BISHELF_SHIFT);
-#endif
-
- if (dsp->channels_process)
- dsp->channels_process(chunk, t2);
-
- if (dsp->compressor_process)
- dsp->compressor_process(chunk, &dsp->data, t2);
-
- dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst);
-
- written += chunk;
- dst += chunk * sizeof (int16_t) * 2;
-
- /* yield at least once each tick */
- tick = current_tick;
- if (TIME_AFTER(tick, last_yield))
- {
- last_yield = tick;
- yield();
- }
- }
- }
-
-#if defined(CPU_COLDFIRE)
- /* set old macsr again */
- coldfire_set_macsr(old_macsr);
-#endif
- return written;
-}
-
-/* Given count number of input samples, calculate the maximum number of
- * samples of output data that would be generated (the calculation is not
- * entirely exact and rounds upwards to be on the safe side; during
- * resampling, the number of samples generated depends on the current state
- * of the resampler).
- */
-/* dsp_input_size MUST be called afterwards */
-int dsp_output_count(struct dsp_config *dsp, int count)
-{
-#ifdef HAVE_PITCHSCREEN
- if (dsp->tdspeed_active)
- count = tdspeed_est_output_size();
-#endif
- if (dsp->resample)
- {
- count = (int)(((unsigned long)count * NATIVE_FREQUENCY
- + (dsp->frequency - 1)) / dsp->frequency);
- }
-
- /* Now we have the resampled sample count which must not exceed
- * resample_buf_count to avoid resample buffer overflow. One
- * must call dsp_input_count() to get the correct input sample
- * count.
- */
- if (count > resample_buf_count)
- count = resample_buf_count;
-
- return count;
-}
-
-/* Given count output samples, calculate number of input samples
- * that would be consumed in order to fill the output buffer.
- */
-int dsp_input_count(struct dsp_config *dsp, int count)
-{
- /* count is now the number of resampled input samples. Convert to
- original input samples. */
- if (dsp->resample)
- {
- /* Use the real resampling delta =
- * dsp->frequency * 65536 / NATIVE_FREQUENCY, and
- * round towards zero to avoid buffer overflows. */
- count = (int)(((unsigned long)count *
- dsp->data.resample_data.delta) >> 16);
- }
-
-#ifdef HAVE_PITCHSCREEN
- if (dsp->tdspeed_active)
- count = tdspeed_est_input_size(count);
-#endif
-
- return count;
-}
-
-static void dsp_set_gain_var(long *var, long value)
-{
- *var = value;
- new_gain = true;
-}
-
-static void dsp_update_functions(struct dsp_config *dsp)
-{
- sample_input_new_format(dsp);
- sample_output_new_format(dsp);
- if (dsp == &AUDIO_DSP)
- dsp_set_crossfeed(crossfeed_enabled);
-}
-
-intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
-{
- switch (setting)
- {
- case DSP_MYDSP:
- switch (value)
- {
- case CODEC_IDX_AUDIO:
- return (intptr_t)&AUDIO_DSP;
- case CODEC_IDX_VOICE:
- return (intptr_t)&VOICE_DSP;
- default:
- return (intptr_t)NULL;
- }
-
- case DSP_SET_FREQUENCY:
- memset(&dsp->data.resample_data, 0, sizeof (dsp->data.resample_data));
- /* Fall through!!! */
- case DSP_SWITCH_FREQUENCY:
- dsp->codec_frequency = (value == 0) ? NATIVE_FREQUENCY : value;
- /* Account for playback speed adjustment when setting dsp->frequency
- if we're called from the main audio thread. Voice UI thread should
- not need this feature.
- */
-#ifdef HAVE_PITCHSCREEN
- if (dsp == &AUDIO_DSP)
- dsp->frequency = pitch_ratio * dsp->codec_frequency / PITCH_SPEED_100;
- else
-#endif
- dsp->frequency = dsp->codec_frequency;
-
- resampler_new_delta(dsp);
-#ifdef HAVE_PITCHSCREEN
- tdspeed_setup(dsp);
-#endif
- break;
-
- case DSP_SET_SAMPLE_DEPTH:
- dsp->sample_depth = value;
-
- if (dsp->sample_depth <= NATIVE_DEPTH)
- {
- dsp->data.frac_bits = WORD_FRACBITS;
- dsp->sample_bytes = sizeof (int16_t); /* samples are 16 bits */
- dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1);
- dsp->data.clip_min = -((1 << WORD_FRACBITS));
- }
- else
- {
- dsp->data.frac_bits = value;
- dsp->sample_bytes = sizeof (int32_t); /* samples are 32 bits */
- dsp->data.clip_max = (1 << value) - 1;
- dsp->data.clip_min = -(1 << value);
- }
-
- dsp->data.output_scale = dsp->data.frac_bits + 1 - NATIVE_DEPTH;
- sample_input_new_format(dsp);
- dither_init(dsp);
- break;
-
- case DSP_SET_STEREO_MODE:
- dsp->stereo_mode = value;
- dsp->data.num_channels = value == STEREO_MONO ? 1 : 2;
- dsp_update_functions(dsp);
-#ifdef HAVE_PITCHSCREEN
- tdspeed_setup(dsp);
-#endif
- break;
-
- case DSP_RESET:
- dsp->stereo_mode = STEREO_NONINTERLEAVED;
- dsp->data.num_channels = 2;
- dsp->sample_depth = NATIVE_DEPTH;
- dsp->data.frac_bits = WORD_FRACBITS;
- dsp->sample_bytes = sizeof (int16_t);
- dsp->data.output_scale = dsp->data.frac_bits + 1 - NATIVE_DEPTH;
- dsp->data.clip_max = ((1 << WORD_FRACBITS) - 1);
- dsp->data.clip_min = -((1 << WORD_FRACBITS));
- dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY;
-
- if (dsp == &AUDIO_DSP)
- {
- track_gain = 0;
- album_gain = 0;
- track_peak = 0;
- album_peak = 0;
- new_gain = true;
- }
-
- dsp_update_functions(dsp);
- resampler_new_delta(dsp);
-#ifdef HAVE_PITCHSCREEN
- tdspeed_setup(dsp);
-#endif
- if (dsp == &AUDIO_DSP)
- compressor_reset();
- break;
-
- case DSP_FLUSH:
- memset(&dsp->data.resample_data, 0,
- sizeof (dsp->data.resample_data));
- resampler_new_delta(dsp);
- dither_init(dsp);
-#ifdef HAVE_PITCHSCREEN
- tdspeed_setup(dsp);
-#endif
- if (dsp == &AUDIO_DSP)
- compressor_reset();
- break;
-
- case DSP_SET_TRACK_GAIN:
- if (dsp == &AUDIO_DSP)
- dsp_set_gain_var(&track_gain, value);
- break;
-
- case DSP_SET_ALBUM_GAIN:
- if (dsp == &AUDIO_DSP)
- dsp_set_gain_var(&album_gain, value);
- break;
-
- case DSP_SET_TRACK_PEAK:
- if (dsp == &AUDIO_DSP)
- dsp_set_gain_var(&track_peak, value);
- break;
-
- case DSP_SET_ALBUM_PEAK:
- if (dsp == &AUDIO_DSP)
- dsp_set_gain_var(&album_peak, value);
- break;
-
- default:
- return 0;
- }
-
- return 1;
-}
-
-int get_replaygain_mode(bool have_track_gain, bool have_album_gain)
-{
- int type;
-
- bool track = ((global_settings.replaygain_type == REPLAYGAIN_TRACK)
- || ((global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
- && global_settings.playlist_shuffle));
-
- type = (!track && have_album_gain) ? REPLAYGAIN_ALBUM
- : have_track_gain ? REPLAYGAIN_TRACK : -1;
-
- return type;
-}
-
-void dsp_set_replaygain(void)
-{
- long gain = 0;
-
- new_gain = false;
-
- if ((global_settings.replaygain_type != REPLAYGAIN_OFF) ||
- global_settings.replaygain_noclip)
- {
- bool track_mode = get_replaygain_mode(track_gain != 0,
- album_gain != 0) == REPLAYGAIN_TRACK;
- long peak = (track_mode || !album_peak) ? track_peak : album_peak;
-
- if (global_settings.replaygain_type != REPLAYGAIN_OFF)
- {
- gain = (track_mode || !album_gain) ? track_gain : album_gain;
-
- if (global_settings.replaygain_preamp)
- {
- long preamp = get_replaygain_int(
- global_settings.replaygain_preamp * 10);
-
- gain = (long) (((int64_t) gain * preamp) >> 24);
- }
- }
-
- if (gain == 0)
- {
- /* So that noclip can work even with no gain information. */
- gain = DEFAULT_GAIN;
- }
-
- if (global_settings.replaygain_noclip && (peak != 0)
- && ((((int64_t) gain * peak) >> 24) >= DEFAULT_GAIN))
- {
- gain = (((int64_t) DEFAULT_GAIN << 24) / peak);
- }
-
- if (gain == DEFAULT_GAIN)
- {
- /* Nothing to do, disable processing. */
- gain = 0;
- }
- }
-
- /* Store in S7.24 format to simplify calculations. */
- replaygain = gain;
- set_gain(&AUDIO_DSP);
-}
-
-/** SET COMPRESSOR
- * Called by the menu system to configure the compressor process */
-void dsp_set_compressor(const struct compressor_settings *settings)
-{
- /* enable/disable the compressor */
- AUDIO_DSP.compressor_process = compressor_update(settings) ?
- compressor_process : NULL;
-}
diff --git a/lib/rbcodec/dsp/dsp.h b/lib/rbcodec/dsp/dsp.h
index a99df17468..feac4aa845 100644
--- a/lib/rbcodec/dsp/dsp.h
+++ b/lib/rbcodec/dsp/dsp.h
@@ -18,109 +18,159 @@
* KIND, either express or implied.
*
****************************************************************************/
-
#ifndef _DSP_H
#define _DSP_H
-#include <stdlib.h>
-#include <stdbool.h>
+struct dsp_config;
-#define NATIVE_FREQUENCY 44100
+/* Include all this junk here for now */
+#include "dsp_proc_settings.h"
-enum
+enum dsp_ids
{
- STEREO_INTERLEAVED = 0,
- STEREO_NONINTERLEAVED,
- STEREO_MONO,
- STEREO_NUM_MODES,
-};
-
-enum
-{
- CODEC_IDX_AUDIO = 0,
+ CODEC_IDX_AUDIO,
CODEC_IDX_VOICE,
+ DSP_COUNT,
};
-enum
+enum dsp_settings
{
- DSP_MYDSP = 1,
+ DSP_INIT, /* For dsp_init */
+ DSP_RESET,
DSP_SET_FREQUENCY,
- DSP_SWITCH_FREQUENCY,
+ DSP_SWITCH_FREQUENCY = DSP_SET_FREQUENCY, /* deprecated */
DSP_SET_SAMPLE_DEPTH,
DSP_SET_STEREO_MODE,
- DSP_RESET,
DSP_FLUSH,
- DSP_SET_TRACK_GAIN,
- DSP_SET_ALBUM_GAIN,
- DSP_SET_TRACK_PEAK,
- DSP_SET_ALBUM_PEAK,
- DSP_CROSSFEED
+ DSP_PROC_INIT,
+ DSP_PROC_CLOSE,
+ DSP_PROC_SETTING, /* stage-specific should be this + id */
};
+#define NATIVE_FREQUENCY 44100 /* internal/output sample rate */
-/****************************************************************************
- * NOTE: Any assembly routines that use these structures must be updated
- * if current data members are moved or changed.
- */
-struct resample_data
+enum dsp_stereo_modes
{
- uint32_t delta; /* 00h */
- uint32_t phase; /* 04h */
- int32_t last_sample[2]; /* 08h */
- /* 10h */
+ STEREO_INTERLEAVED,
+ STEREO_NONINTERLEAVED,
+ STEREO_MONO,
+ STEREO_NUM_MODES,
};
-/* This is for passing needed data to external dsp routines. If another
- * dsp parameter needs to be passed, add to the end of the structure
- * and remove from dsp_config.
- * If another function type becomes assembly/external and requires dsp
- * config info, add a pointer paramter of type "struct dsp_data *".
- * If removing something from other than the end, reserve the spot or
- * else update every implementation for every target.
- * Be sure to add the offset of the new member for easy viewing as well. :)
- * It is the first member of dsp_config and all members can be accessesed
- * through the main aggregate but this is intended to make a safe haven
- * for these items whereas the c part can be rearranged at will. dsp_data
- * could even moved within dsp_config without disurbing the order.
- */
-struct dsp_data
+/* Format into for the buffer (if .valid == true) */
+struct sample_format
{
- int output_scale; /* 00h */
- int num_channels; /* 04h */
- struct resample_data resample_data; /* 08h */
- int32_t clip_min; /* 18h */
- int32_t clip_max; /* 1ch */
- int32_t gain; /* 20h - Note that this is in S8.23 format. */
- int frac_bits; /* 24h */
- /* 28h */
+ uint8_t changed; /* 00h: 0=no change, 1=changed (is also index) */
+ uint8_t num_channels; /* 01h: number of channels of data */
+ uint8_t frac_bits; /* 02h: number of fractional bits */
+ uint8_t output_scale; /* 03h: output scaling shift */
+ int32_t frequency; /* 04h: pitch-adjusted sample rate */
+ int32_t codec_frequency; /* 08h: codec-specifed sample rate */
+ /* 0ch */
};
-struct dsp_config;
+/* Compare format data only */
+#define EQU_SAMPLE_FORMAT(f1, f2) \
+ (!memcmp(&(f1).num_channels, &(f2).num_channels, \
+ sizeof (f1) - sizeof ((f1).changed)))
+
+static inline void format_change_set(struct sample_format *f)
+ { f->changed = 1; }
+static inline void format_change_ack(struct sample_format *f)
+ { f->changed = 0; }
-int dsp_process(struct dsp_config *dsp, char *dest,
- const char *src[], int count);
-int dsp_input_count(struct dsp_config *dsp, int count);
-int dsp_output_count(struct dsp_config *dsp, int count);
-intptr_t dsp_configure(struct dsp_config *dsp, int setting,
+/* Used by ASM routines - keep field order or else fix the functions */
+struct dsp_buffer
+{
+ int32_t remcount; /* 00h: Samples in buffer (In, Int, Out) */
+ union
+ {
+ const void *pin[2]; /* 04h: Channel pointers (In) */
+ int32_t *p32[2]; /* 04h: Channel pointers (Int) */
+ int16_t *p16out; /* 04h: DSP output buffer (Out) */
+ };
+ union
+ {
+ uint32_t proc_mask; /* 0Ch: In-place effects already appled to buffer
+ in order to avoid double-processing. Set
+ to zero on new buffer before passing to
+ DSP. */
+ int bufcount; /* 0Ch: Buffer length/dest buffer remaining
+ Basically, pay no attention unless it's
+ *your* new buffer and is used internally
+ or is specifically the final output
+ buffer. */
+ };
+ struct sample_format format; /* 10h: Buffer format data */
+ /* 1ch */
+};
+
+/* Remove samples from input buffer (In). Sample size is specified.
+ Provided to dsp_process(). */
+static inline void dsp_advance_buffer_input(struct dsp_buffer *buf,
+ int by_count,
+ size_t size_each)
+{
+ buf->remcount -= by_count;
+ buf->pin[0] += by_count * size_each;
+ buf->pin[1] += by_count * size_each;
+}
+
+/* Add samples to output buffer and update remaining space (Out).
+ Provided to dsp_process() */
+static inline void dsp_advance_buffer_output(struct dsp_buffer *buf,
+ int by_count)
+{
+ buf->bufcount -= by_count;
+ buf->remcount += by_count;
+ buf->p16out += 2 * by_count; /* Interleaved stereo */
+}
+
+/* Remove samples from internal input buffer (In, Int).
+ Provided to dsp_process() or by another processing stage. */
+static inline void dsp_advance_buffer32(struct dsp_buffer *buf,
+ int by_count)
+{
+ buf->remcount -= by_count;
+ buf->p32[0] += by_count;
+ buf->p32[1] += by_count;
+}
+
+/** For use by processing stages **/
+
+#define DSP_PRINT_FORMAT(name, id, format) \
+ DEBUGF("DSP format- " #name "\n" \
+ " id:%d chg:%c ch:%u fb:%u os:%u hz:%u chz:%u\n", \
+ (int)id, \
+ (format).changed ? 'y' : 'n', \
+ (unsigned int)(format).num_channels, \
+ (unsigned int)(format).frac_bits, \
+ (unsigned int)(format).output_scale, \
+ (unsigned int)(format).frequency, \
+ (unsigned int)(format).codec_frequency);
+
+/* Get DSP pointer */
+struct dsp_config * dsp_get_config(enum dsp_ids id);
+
+/* Get DSP id */
+enum dsp_ids dsp_get_id(const struct dsp_config *dsp);
+
+#if 0 /* Not needed now but enable if something must know this */
+/* Is the DSP processing a buffer? */
+bool dsp_is_busy(const struct dsp_config *dsp);
+#endif /* 0 */
+
+/** General DSP processing **/
+
+/* Process the given buffer - see implementation in dsp.c for more */
+void dsp_process(struct dsp_config *dsp, struct dsp_buffer *src,
+ struct dsp_buffer *dst);
+
+/* Change DSP settings */
+intptr_t dsp_configure(struct dsp_config *dsp, unsigned int setting,
intptr_t value);
-int get_replaygain_mode(bool have_track_gain, bool have_album_gain);
-void dsp_set_replaygain(void);
-void dsp_set_crossfeed(bool enable);
-void dsp_set_crossfeed_direct_gain(int gain);
-void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain,
- long cutoff);
-void dsp_set_eq(bool enable);
-void dsp_set_eq_precut(int precut);
-void dsp_set_eq_coefs(int band, int cutoff, int q, int gain);
-void dsp_dither_enable(bool enable);
-void dsp_timestretch_enable(bool enable);
-bool dsp_timestretch_available(void);
-void sound_set_pitch(int32_t r);
-int32_t sound_get_pitch(void);
-void dsp_set_timestretch(int32_t percent);
-int32_t dsp_get_timestretch(void);
-int dsp_callback(int msg, intptr_t param);
-struct compressor_settings;
-void dsp_set_compressor(const struct compressor_settings *settings);
-
-#endif
+
+/* One-time startup init that must come before settings reset/apply */
+void dsp_init(void);
+
+#endif /* _DSP_H */
diff --git a/lib/rbcodec/dsp/dsp_arm.S b/lib/rbcodec/dsp/dsp_arm.S
index 685aca411c..9fd19ae108 100644
--- a/lib/rbcodec/dsp/dsp_arm.S
+++ b/lib/rbcodec/dsp/dsp_arm.S
@@ -21,20 +21,19 @@
#include "config.h"
/****************************************************************************
- * void channels_process_sound_chan_mono(int count, int32_t *buf[])
+ * void channel_mode_proc_mono(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
*/
-
-#include "config.h"
-
- .section .icode, "ax", %progbits
- .align 2
- .global channels_process_sound_chan_mono
- .type channels_process_sound_chan_mono, %function
-channels_process_sound_chan_mono:
- @ input: r0 = count, r1 = buf
+ .section .icode
+ .global channel_mode_proc_mono
+ .type channel_mode_proc_mono, %function
+channel_mode_proc_mono:
+ @ input: r0 = this, r1 = buf_p
+ ldr r1, [r1] @ r1 = buf = *buf_p;
stmfd sp!, { r4, lr } @
@
- ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1]
+ ldmia r1, { r0-r2 } @ r0 = buf->remcount, r1 = buf->p32[0],
+ @ r2 = buf->p32[1]
subs r0, r0, #1 @ odd: end at 0; even: end at -1
beq .mono_singlesample @ Zero? Only one sample!
@
@@ -61,25 +60,26 @@ channels_process_sound_chan_mono:
str r12, [r2] @ store Mo
@
ldmpc regs=r4 @
- .size channels_process_sound_chan_mono, \
- .-channels_process_sound_chan_mono
+ .size channel_mode_proc_mono, .-channel_mode_proc_mono
/****************************************************************************
- * void channels_process_sound_chan_custom(int count, int32_t *buf[])
+ * void channel_mode_proc_custom(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
*/
- .section .icode, "ax", %progbits
- .align 2
- .global channels_process_sound_chan_custom
- .type channels_process_sound_chan_custom, %function
-channels_process_sound_chan_custom:
+ .section .icode
+ .global channel_mode_proc_custom
+ .type channel_mode_proc_custom, %function
+channel_mode_proc_custom:
+ @ input: r0 = this, r1 = buf_p
+ ldr r2, [r0] @ r2 = &channel_mode_data = this->data
+ ldr r1, [r1] @ r1 = buf = *buf_p;
+
stmfd sp!, { r4-r10, lr }
- ldr r3, =dsp_sw_gain
- ldr r4, =dsp_sw_cross
+ ldmia r2, { r3, r4 } @ r3 = sw_gain, r4 = sw_cross
- ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1]
- ldr r3, [r3] @ r3 = dsp_sw_gain
- ldr r4, [r4] @ r4 = dsp_sw_cross
+ ldmia r1, { r0-r2 } @ r0 = buf->remcount, r1 = buf->p32[0],
+ @ r2 = buf->p32[1]
subs r0, r0, #1
beq .custom_single_sample @ Zero? Only one sample!
@@ -135,21 +135,22 @@ channels_process_sound_chan_custom:
str r7, [r2] @ Store Rc0
ldmpc regs=r4-r10
- .size channels_process_sound_chan_custom, \
- .-channels_process_sound_chan_custom
+ .size channel_mode_proc_custom, .-channel_mode_proc_custom
/****************************************************************************
- * void channels_process_sound_chan_karaoke(int count, int32_t *buf[])
+ * void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
*/
- .section .icode, "ax", %progbits
- .align 2
- .global channels_process_sound_chan_karaoke
- .type channels_process_sound_chan_karaoke, %function
-channels_process_sound_chan_karaoke:
- @ input: r0 = count, r1 = buf
+ .section .icode
+ .global channel_mode_proc_karaoke
+ .type channel_mode_proc_karaoke, %function
+channel_mode_proc_karaoke:
+ @ input: r0 = this, r1 = buf_p
+ ldr r1, [r1] @ r1 = buf = *buf_p;
stmfd sp!, { r4, lr } @
@
- ldmia r1, { r1, r2 } @ r1 = buf[0], r2 = buf[1]
+ ldmia r1, { r0-r2 } @ r0 = buf->remcount, r1 = buf->p32[0],
+ @ r2 = buf->p32[1]
subs r0, r0, #1 @ odd: end at 0; even: end at -1
beq .karaoke_singlesample @ Zero? Only one sample!
@
@@ -179,24 +180,313 @@ channels_process_sound_chan_karaoke:
str r12, [r2] @ store Ro
@
ldmpc regs=r4 @
- .size channels_process_sound_chan_karaoke, \
- .-channels_process_sound_chan_karaoke
+ .size channel_mode_proc_karaoke, .-channel_mode_proc_karaoke
+
+/****************************************************************************
+ * void crossfeed_process(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
+ */
+ .section .text
+ .global crossfeed_process
+crossfeed_process:
+ @ input: r0 = this, r1 = buf_p
+ @ unfortunately, we ended up in a bit of a register squeeze here, and need
+ @ to keep the count on the stack :/
+ ldr r1, [r1] @ r1 = buf = *buf_p;
+ stmfd sp!, { r4-r11, lr } @ stack modified regs
+ ldr r12, [r1] @ r12 = buf->remcount
+ ldr r14, [r0] @ r14 = this->data = &crossfeed_state
+ ldmib r1, { r2-r3 } @ r2 = buf->p32[0], r3 = buf->p32[1]
+ ldmia r14!, { r4-r11 } @ load direct gain and filter data
+ add r0, r14, #13*2*4 @ calculate end of delay
+ stmfd sp!, { r0, r12 } @ stack end of delay adr, count and state
+ ldr r0, [r0] @ fetch current delay line address
+
+ /* Register usage in loop:
+ * r0 = &delay[index][0], r1 = accumulator high, r2 = buf->p32[0],
+ * r3 = buf->p32[1], r4 = direct gain, r5-r7 = b0, b1, a1 (filter coefs),
+ * r8-r11 = filter history, r12 = temp, r14 = accumulator low
+ */
+.cfloop:
+ smull r14, r1, r6, r8 @ acc = b1*dr[n - 1]
+ smlal r14, r1, r7, r9 @ acc += a1*y_l[n - 1]
+ ldr r8, [r0, #4] @ r8 = dr[n]
+ smlal r14, r1, r5, r8 @ acc += b0*dr[n]
+ mov r9, r1, lsl #1 @ fix format for filter history
+ ldr r12, [r2] @ load left input
+ smlal r14, r1, r4, r12 @ acc += gain*x_l[n]
+ mov r1, r1, lsl #1 @ fix format
+ str r1, [r2], #4 @ save result
+
+ smull r14, r1, r6, r10 @ acc = b1*dl[n - 1]
+ smlal r14, r1, r7, r11 @ acc += a1*y_r[n - 1]
+ ldr r10, [r0] @ r10 = dl[n]
+ str r12, [r0], #4 @ save left input to delay line
+ smlal r14, r1, r5, r10 @ acc += b0*dl[n]
+ mov r11, r1, lsl #1 @ fix format for filter history
+ ldr r12, [r3] @ load right input
+ smlal r14, r1, r4, r12 @ acc += gain*x_r[n]
+ str r12, [r0], #4 @ save right input to delay line
+ mov r1, r1, lsl #1 @ fix format
+ ldmia sp, { r12, r14 } @ fetch delay line end addr and count from stack
+ str r1, [r3], #4 @ save result
+
+ cmp r0, r12 @ need to wrap to start of delay?
+ subhs r0, r12, #13*2*4 @ wrap back delay line ptr to start
+
+ subs r14, r14, #1 @ are we finished?
+ strgt r14, [sp, #4] @ nope, save count back to stack
+ bgt .cfloop
+
+ @ save data back to struct
+ str r0, [r12] @ save delay line index
+ sub r12, r12, #13*2*4 + 4*4 @ r12 = data->history
+ stmia r12, { r8-r11 } @ save filter history
+ add sp, sp, #8 @ remove temp variables from stack
+ ldmpc regs=r4-r11
+ .size crossfeed_process, .-crossfeed_process
+
+/****************************************************************************
+ * int lin_resample_resample(struct resample_data *data,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
+ */
+ .section .text
+ .global lin_resample_resample
+lin_resample_resample:
+ @input: r0 = data, r1 = src, r2 = dst
+ stmfd sp!, { r4-r11, lr } @ stack modified regs
+ ldr r4, [r0] @ r4 = data->delta
+ add r10, r0, #4 @ r10 = &data->phase
+ ldrb r3, [r1, #17] @ r3 = num_channels,
+ stmfd sp!, { r1, r10 } @ stack src, &data->phase
+.lrs_channel_loop:
+ ldr r5, [r10] @ r5 = data->phase
+ ldr r6, [r1] @ r6 = srcrem = src->remcount
+ ldr r7, [r1, r3, lsl #2] @ r7 = src->p32[ch]
+ ldr r8, [r2, r3, lsl #2] @ r8 = dst->p32[ch]
+ ldr r9, [r2, #12] @ r9 = dstrem = dst->bufcount
+
+ cmp r6, #0x8000 @ srcrem = MIN(srcrem, 0x8000)
+ movgt r6, #0x8000 @
+ mov r0, r5, lsr #16 @ pos = MIN(pos, srcrem)
+ cmp r0, r6 @
+ movgt r0, r6 @ r0 = pos = phase >> 16
+ cmp r0, #0 @
+ ldrle r11, [r10, r3, lsl #2] @ pos <= 0? r11 = last = last_sample[ch]
+ addgt r12, r7, r0, lsl #2 @ pos > 0? r1 = last = s[pos - 1]
+ ldrgt r11, [r12, #-4] @
+ cmp r0, r6 @
+ bge .lrs_channel_done @ pos >= count? channel complete
+
+ cmp r4, #0x10000 @ delta >= 1.0?
+ ldrhs r12, [r7, r0, lsl #2] @ yes? r12 = s[pos]
+ bhs .lrs_dsstart @ yes? is downsampling
+
+ /** Upsampling **/
+ mov r5, r5, lsl #16 @ Move phase into high halfword
+ add r7, r7, r0, lsl #2 @ r7 = &s[pos]
+ sub r0, r6, r0 @ r0 = dte = srcrem - pos
+.lrs_usloop_1:
+ ldr r12, [r7], #4 @ r12 = s[pos]
+ sub r14, r12, r11 @ r14 = diff = s[pos] - s[pos - 1]
+.lrs_usloop_0:
+ mov r1, r5, lsr #16 @ r1 = frac = phase >> 16
+ @ keep frac in Rs to take advantage of multiplier early termination
+ smull r1, r10, r14, r1 @ r1, r10 = diff * frac (lo, hi)
+ add r1, r11, r1, lsr #16 @ r1 = out = last + frac*diff
+ add r1, r1, r10, lsl #16 @
+ str r1, [r8], #4 @ *d++ = out
+ subs r9, r9, #1 @ destination full?
+ bls .lrs_usfull @ yes? channel is done
+ adds r5, r5, r4, lsl #16 @ phase += delta << 16
+ bcc .lrs_usloop_0 @ if carry is set, pos is incremented
+ subs r0, r0, #1 @ if srcrem > 0, do another sample
+ mov r11, r12 @ r11 = last = s[pos-1] (pos changed)
+ bgt .lrs_usloop_1
+ b .lrs_usdone
+
+.lrs_usfull:
+ adds r5, r5, r4, lsl #16 @ do missed phase increment
+ subcs r0, r0, #1 @ do missed srcrem decrement
+ movcs r11, r12 @ r11 = s[pos-1] (pos changed)
+
+.lrs_usdone:
+ sub r0, r6, r0 @ r0 = pos = srcrem - dte
+ orr r5, r5, r0 @ reconstruct swapped phase
+ mov r5, r5, ror #16 @ swap pos and frac for phase
+ b .lrs_channel_done @
+
+ /** Downsampling **/
+.lrs_dsloop:
+ add r10, r7, r0, lsl #2 @ r10 = &s[pos]
+ ldmda r10, { r11, r12 } @ r11 = last, r12 = s[pos]
+.lrs_dsstart:
+ sub r14, r12, r11 @ r14 = diff = s[pos] - s[pos - 1]
+ @ keep frac in Rs to take advantage of multiplier early termination
+ bic r1, r5, r0, lsl #16 @ frac = phase & 0xffff
+ smull r1, r10, r14, r1 @ r1, r10 = diff * frac (lo, hi)
+ add r5, r5, r4 @ phase += delta
+ subs r9, r9, #1 @ destination full? ...
+ mov r0, r5, lsr #16 @ pos = phase >> 16
+ add r1, r11, r1, lsr #16 @ r1 = out = last + frac*diff
+ add r1, r1, r10, lsl #16 @
+ str r1, [r8], #4 @ *d++ = out
+ cmpgt r6, r0 @ ... || pos >= srcrem? ...
+ bgt .lrs_dsloop @ ... no, do more samples
+
+ cmp r0, r6 @ pos = MIN(pos, srcrem)
+ movgt r0, r6 @
+ sub r1, r0, #1 @ pos must always be > 0 since step >= 1.0
+ ldr r11, [r7, r1, lsl #2] @ r11 = s[pos - 1]
+
+.lrs_channel_done:
+ ldmia sp, { r1, r10 } @ recover src, &data->phase
+ str r11, [r10, r3, lsl #2] @ last_sample[ch] = last
+ subs r3, r3, #1 @
+ bgt .lrs_channel_loop @
+
+ ldr r6, [r2, #12] @ r6 = dst->bufcount
+ sub r5, r5, r0, lsl #16 @ r5 = phase - (pos << 16)
+ str r5, [r10] @ data->phase = r5
+ sub r6, r6, r9 @ r6 = dst->bufcount - dstrem = dstcount
+ str r6, [r2] @ dst->remcount = dstcount
+ add sp, sp, #8 @ adjust stack for temp variables
+ ldmpc regs=r4-r11 @ ... and we're out
+ .size lin_resample_resample, .-lin_resample_resample
+
+/****************************************************************************
+ * void pga_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
+ */
+ .section .icode
+ .global pga_process
+ .type pga_process, %function
+pga_process:
+ @ input: r0 = this, r1 = buf_p
+ ldr r0, [r0] @ r0 = data = this->data (&pga_data)
+ ldr r1, [r1] @ r1 = buf = *buf_p;
+ stmfd sp!, { r4-r8, lr }
+
+ ldr r4, [r0] @ r4 = data->gain
+ ldr r0, [r1], #4 @ r0 = buf->remcount, r1 = buf->p32
+ ldrb r3, [r1, #13] @ r3 = buf->format.num_channels
+
+.pga_channelloop:
+ ldr r2, [r1], #4 @ r2 = buf->p32[ch] and inc index of p32
+ subs r12, r0, #1 @ r12 = count - 1
+ beq .pga_singlesample @ Zero? Only one sample!
+
+.pga_loop:
+ ldmia r2, { r5, r6 } @ load r5, r6 from r2 (*p32[ch])
+ smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8)
+ smull r14, r5, r6, r4 @ r14 = FRACMUL_SHL(r6, r4, 8)
+ subs r12, r12, #2
+ mov r7, r7, lsr #23
+ mov r14, r14, lsr #23
+ orr r7, r7, r8, asl #9
+ orr r14, r14, r5, asl #9
+ stmia r2!, { r7, r14 } @ save r7, r14 to *p32[ch] and increment
+ bgt .pga_loop @ end of pga loop
+
+ blt .pga_evencount @ < 0? even count
+
+.pga_singlesample:
+ ldr r5, [r2] @ handle odd sample
+ smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8)
+ mov r7, r7, lsr #23
+ orr r7, r7, r8, asl #9
+ str r7, [r2]
+
+.pga_evencount:
+ subs r3, r3, #1
+ bgt .pga_channelloop @ end of channel loop
+
+ ldmpc regs=r4-r8
+ .size pga_process, .-pga_process
+
+/****************************************************************************
+ * void filter_process(struct dsp_filter *f, int32_t *buf[], int count,
+ * unsigned int channels)
+ *
+ * define HIGH_PRECISION as '1' to make filtering calculate lower bits after
+ * shifting. without this, "shift" - 1 of the lower bits will be lost here.
+ */
+#define HIGH_PRECISION 0
+
+#if CONFIG_CPU == PP5002
+ .section .icode,"ax",%progbits
+#else
+ .text
+#endif
+ .global filter_process
+filter_process:
+ @input: r0 = f, r1 = buf, r2 = count, r3 = channels
+ stmfd sp!, { r4-r11, lr } @ save all clobbered regs
+ ldmia r0!, { r4-r8 } @ load coefs, r0 = f->history
+ sub r3, r3, #1 @ r3 = ch = channels - 1
+ stmfd sp!, { r0-r3 } @ save adjusted params
+ ldrb r14, [r0, #32] @ r14 = shift
+
+ @ Channels are processed high to low while history is saved low to high
+ @ It's really noone's business how we do this
+.fp_channelloop:
+ ldmia r0, { r9-r12 } @ load history, r0 = history[channels-ch-1]
+ ldr r3, [r1, r3, lsl #2] @ r3 = buf[ch]
+
+ @ r9-r12 = history, r4-r8 = coefs, r0..r1 = accumulator,
+ @ r2 = number of samples, r3 = buf[ch], r14 = shift amount
+.fp_loop:
+ @ Direct form 1 filtering code.
+ @ y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
+ @ where y[] is output and x[] is input. This is performed out of order to
+ @ reuse registers, we're pretty short on regs.
+ smull r0, r1, r5, r9 @ acc = b1*x[i - 1]
+ smlal r0, r1, r6, r10 @ acc += b2*x[i - 2]
+ mov r10, r9 @ fix input history
+ ldr r9, [r3] @ load input and fix history
+ smlal r0, r1, r7, r11 @ acc += a1*y[i - 1]
+ smlal r0, r1, r8, r12 @ acc += a2*y[i - 2]
+ smlal r0, r1, r4, r9 @ acc += b0*x[i] /* avoid stall on arm9 */
+ mov r12, r11 @ fix output history
+ mov r11, r1, asl r14 @ get upper part of result and shift left
+#if HIGH_PRECISION
+ rsb r1, r14, #32 @ get shift amount for lower part
+ orr r11, r11, r0, lsr r1 @ then mix in correctly shifted lower part
+#endif
+ str r11, [r3], #4 @ save result
+ subs r2, r2, #1 @ are we done with this channel?
+ bgt .fp_loop @
+
+ ldr r3, [sp, #12] @ r3 = ch
+ ldr r0, [sp] @ r0 = history[channels-ch-1]
+ subs r3, r3, #1 @ all channels processed?
+ stmia r0!, { r9-r12 } @ save back history, history++
+ ldmhsib sp, { r1-r2 } @ r1 = buf, r2 = count
+ strhs r3, [sp, #12] @ store ch
+ strhs r0, [sp] @ store history[channels-ch-1]
+ bhs .fp_channelloop
+
+ add sp, sp, #16 @ compensate for temp storage
+ ldmpc regs=r4-r11
+ .size filter_process, .-filter_process
#if ARM_ARCH < 6
/****************************************************************************
- * void sample_output_mono(int count, struct dsp_data *data,
- * const int32_t *src[], int16_t *dst)
+ * void sample_output_mono(struct sample_io_data *this,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
*/
- .section .icode, "ax", %progbits
- .align 2
+ .section .icode
.global sample_output_mono
.type sample_output_mono, %function
sample_output_mono:
- @ input: r0 = count, r1 = data, r2 = src, r3 = dst
+ @ input: r0 = this, r1 = src, r2 = dst
stmfd sp!, { r4-r6, lr }
- ldr r1, [r1] @ lr = data->output_scale
- ldr r2, [r2] @ r2 = src[0]
+ ldr r0, [r0] @ r0 = this->outcount
+ ldr r3, [r2, #4] @ r2 = dst->p16out
+ ldr r2, [r1, #4] @ r1 = src->p32[0]
+ ldrb r1, [r1, #19] @ r2 = src->format.output_scale
mov r4, #1
mov r4, r4, lsl r1 @ r4 = 1 << (scale-1)
@@ -246,19 +536,21 @@ sample_output_mono:
.size sample_output_mono, .-sample_output_mono
/****************************************************************************
- * void sample_output_stereo(int count, struct dsp_data *data,
- * const int32_t *src[], int16_t *dst)
+ * void sample_output_stereo(struct sample_io_data *this,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
*/
- .section .icode, "ax", %progbits
- .align 2
+ .section .icode
.global sample_output_stereo
.type sample_output_stereo, %function
sample_output_stereo:
- @ input: r0 = count, r1 = data, r2 = src, r3 = dst
+ @ input: r0 = this, r1 = src, r2 = dst
stmfd sp!, { r4-r9, lr }
- ldr r1, [r1] @ r1 = data->output_scale
- ldmia r2, { r2, r5 } @ r2 = src[0], r5 = src[1]
+ ldr r0, [r0] @ r0 = this->outcount
+ ldr r3, [r2, #4] @ r3 = dsp->p16out
+ ldmib r1, { r2, r5 } @ r2 = src->p32[0], r5 = src->p32[1]
+ ldrb r1, [r1, #19] @ r1 = src->format.output_scale
mov r4, #1
mov r4, r4, lsl r1 @ r4 = 1 << (scale-1)
@@ -330,232 +622,3 @@ sample_output_stereo:
ldmpc regs=r4-r9
.size sample_output_stereo, .-sample_output_stereo
#endif /* ARM_ARCH < 6 */
-
-/****************************************************************************
- * void apply_crossfeed(int count, int32_t* src[])
- */
- .section .text
- .global apply_crossfeed
-apply_crossfeed:
- @ unfortunately, we ended up in a bit of a register squeeze here, and need
- @ to keep the count on the stack :/
- stmdb sp!, { r4-r11, lr } @ stack modified regs
- ldmia r1, { r2-r3 } @ r2 = src[0], r3 = src[1]
-
- ldr r1, =crossfeed_data
- ldmia r1!, { r4-r11 } @ load direct gain and filter data
- mov r12, r0 @ better to ldm delay + count later
- add r0, r1, #13*4*2 @ calculate end of delay
- stmdb sp!, { r0, r12 } @ stack end of delay adr and count
- ldr r0, [r1, #13*4*2] @ fetch current delay line address
-
- /* Register usage in loop:
- * r0 = &delay[index][0], r1 = accumulator high, r2 = src[0], r3 = src[1],
- * r4 = direct gain, r5-r7 = b0, b1, a1 (filter coefs),
- * r8-r11 = filter history, r12 = temp, r14 = accumulator low
- */
-.cfloop:
- smull r14, r1, r6, r8 @ acc = b1*dr[n - 1]
- smlal r14, r1, r7, r9 @ acc += a1*y_l[n - 1]
- ldr r8, [r0, #4] @ r8 = dr[n]
- smlal r14, r1, r5, r8 @ acc += b0*dr[n]
- mov r9, r1, lsl #1 @ fix format for filter history
- ldr r12, [r2] @ load left input
- smlal r14, r1, r4, r12 @ acc += gain*x_l[n]
- mov r1, r1, lsl #1 @ fix format
- str r1, [r2], #4 @ save result
-
- smull r14, r1, r6, r10 @ acc = b1*dl[n - 1]
- smlal r14, r1, r7, r11 @ acc += a1*y_r[n - 1]
- ldr r10, [r0] @ r10 = dl[n]
- str r12, [r0], #4 @ save left input to delay line
- smlal r14, r1, r5, r10 @ acc += b0*dl[n]
- mov r11, r1, lsl #1 @ fix format for filter history
- ldr r12, [r3] @ load right input
- smlal r14, r1, r4, r12 @ acc += gain*x_r[n]
- str r12, [r0], #4 @ save right input to delay line
- mov r1, r1, lsl #1 @ fix format
- ldmia sp, { r12, r14 } @ fetch delay line end addr and count from stack
- str r1, [r3], #4 @ save result
-
- cmp r0, r12 @ need to wrap to start of delay?
- subeq r0, r0, #13*4*2 @ wrap back delay line ptr to start
-
- subs r14, r14, #1 @ are we finished?
- strne r14, [sp, #4] @ nope, save count back to stack
- bne .cfloop
-
- @ save data back to struct
- ldr r12, =crossfeed_data + 4*4
- stmia r12, { r8-r11 } @ save filter history
- str r0, [r12, #30*4] @ save delay line index
- add sp, sp, #8 @ remove temp variables from stack
- ldmpc regs=r4-r11
- .size apply_crossfeed, .-apply_crossfeed
-
-/****************************************************************************
- * int dsp_downsample(int count, struct dsp_data *data,
- * in32_t *src[], int32_t *dst[])
- */
- .section .text
- .global dsp_downsample
-dsp_downsample:
- stmdb sp!, { r4-r11, lr } @ stack modified regs
- ldmib r1, { r5-r6 } @ r5 = num_channels,r6 = resample_data.delta
- sub r5, r5, #1 @ pre-decrement num_channels for use
- add r4, r1, #12 @ r4 = &resample_data.phase
- mov r12, #0xff
- orr r12, r12, #0xff00 @ r12 = 0xffff
-.dschannel_loop:
- ldr r1, [r4] @ r1 = resample_data.phase
- ldr r7, [r2, r5, lsl #2] @ r7 = s = src[ch - 1]
- ldr r8, [r3, r5, lsl #2] @ r8 = d = dst[ch - 1]
- add r9, r4, #4 @ r9 = &last_sample[0]
- ldr r10, [r9, r5, lsl #2] @ r10 = last_sample[ch - 1]
- sub r11, r0, #1
- ldr r14, [r7, r11, lsl #2] @ load last sample in s[] ...
- str r14, [r9, r5, lsl #2] @ and write as next frame's last_sample
- movs r9, r1, lsr #16 @ r9 = pos = phase >> 16
- ldreq r11, [r7] @ if pos = 0, load src[0] and jump into loop
- beq .dsuse_last_start
- cmp r9, r0 @ if pos >= count, we're already done
- bge .dsloop_skip
-
- @ Register usage in loop:
- @ r0 = count, r1 = phase, r4 = &resample_data.phase, r5 = cur_channel,
- @ r6 = delta, r7 = s, r8 = d, r9 = pos, r10 = s[pos - 1], r11 = s[pos]
-.dsloop:
- add r9, r7, r9, lsl #2 @ r9 = &s[pos]
- ldmda r9, { r10, r11 } @ r10 = s[pos - 1], r11 = s[pos]
-.dsuse_last_start:
- sub r11, r11, r10 @ r11 = diff = s[pos] - s[pos - 1]
- @ keep frac in lower bits to take advantage of multiplier early termination
- and r9, r1, r12 @ frac = phase & 0xffff
- smull r9, r14, r11, r9
- add r1, r1, r6 @ phase += delta
- add r10, r10, r9, lsr #16 @ r10 = out = s[pos - 1] + frac*diff
- add r10, r10, r14, lsl #16
- str r10, [r8], #4 @ *d++ = out
- mov r9, r1, lsr #16 @ pos = phase >> 16
- cmp r9, r0 @ pos < count?
- blt .dsloop @ yup, do more samples
-.dsloop_skip:
- subs r5, r5, #1
- bpl .dschannel_loop @ if (--ch) >= 0, do another channel
- sub r1, r1, r0, lsl #16 @ wrap phase back to start
- str r1, [r4] @ store back
- ldr r1, [r3] @ r1 = &dst[0]
- sub r8, r8, r1 @ dst - &dst[0]
- mov r0, r8, lsr #2 @ convert bytes->samples
- ldmpc regs=r4-r11 @ ... and we're out
- .size dsp_downsample, .-dsp_downsample
-
-/****************************************************************************
- * int dsp_upsample(int count, struct dsp_data *dsp,
- * in32_t *src[], int32_t *dst[])
- */
- .section .text
- .global dsp_upsample
-dsp_upsample:
- stmfd sp!, { r4-r11, lr } @ stack modified regs
- ldmib r1, { r5-r6 } @ r5 = num_channels,r6 = resample_data.delta
- sub r5, r5, #1 @ pre-decrement num_channels for use
- add r4, r1, #12 @ r4 = &resample_data.phase
- mov r6, r6, lsl #16 @ we'll use carry to detect pos increments
- stmfd sp!, { r0, r4 } @ stack count and &resample_data.phase
-.uschannel_loop:
- ldr r12, [r4] @ r12 = resample_data.phase
- ldr r7, [r2, r5, lsl #2] @ r7 = s = src[ch - 1]
- ldr r8, [r3, r5, lsl #2] @ r8 = d = dst[ch - 1]
- add r9, r4, #4 @ r9 = &last_sample[0]
- mov r1, r12, lsl #16 @ we'll use carry to detect pos increments
- sub r11, r0, #1
- ldr r14, [r7, r11, lsl #2] @ load last sample in s[] ...
- ldr r10, [r9, r5, lsl #2] @ r10 = last_sample[ch - 1]
- str r14, [r9, r5, lsl #2] @ and write as next frame's last_sample
- movs r14, r12, lsr #16 @ pos = resample_data.phase >> 16
- beq .usstart_0 @ pos = 0
- cmp r14, r0 @ if pos >= count, we're already done
- bge .usloop_skip
- add r7, r7, r14, lsl #2 @ r7 = &s[pos]
- ldr r10, [r7, #-4] @ r11 = s[pos - 1]
- b .usstart_0
-
- @ Register usage in loop:
- @ r0 = count, r1 = phase, r4 = &resample_data.phase, r5 = cur_channel,
- @ r6 = delta, r7 = s, r8 = d, r9 = diff, r10 = s[pos - 1], r11 = s[pos]
-.usloop_1:
- mov r10, r11 @ r10 = previous sample
-.usstart_0:
- ldr r11, [r7], #4 @ r11 = next sample
- mov r4, r1, lsr #16 @ r4 = frac = phase >> 16
- sub r9, r11, r10 @ r9 = diff = s[pos] - s[pos - 1]
-.usloop_0:
- smull r12, r14, r4, r9
- adds r1, r1, r6 @ phase += delta << 16
- mov r4, r1, lsr #16 @ r4 = frac = phase >> 16
- add r14, r10, r14, lsl #16
- add r14, r14, r12, lsr #16 @ r14 = out = s[pos - 1] + frac*diff
- str r14, [r8], #4 @ *d++ = out
- bcc .usloop_0 @ if carry is set, pos is incremented
- subs r0, r0, #1 @ if count > 0, do another sample
- bgt .usloop_1
-.usloop_skip:
- subs r5, r5, #1
- ldmfd sp, { r0, r4 } @ reload count and &resample_data.phase
- bpl .uschannel_loop @ if (--ch) >= 0, do another channel
- mov r1, r1, lsr #16 @ wrap phase back to start of next frame
- ldr r2, [r3] @ r1 = &dst[0]
- str r1, [r4] @ store phase
- sub r8, r8, r2 @ dst - &dst[0]
- mov r0, r8, lsr #2 @ convert bytes->samples
- add sp, sp, #8 @ adjust stack for temp variables
- ldmpc regs=r4-r11 @ ... and we're out
- .size dsp_upsample, .-dsp_upsample
-
-/****************************************************************************
- * void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[])
- */
- .section .icode, "ax", %progbits
- .align 2
- .global dsp_apply_gain
- .type dsp_apply_gain, %function
-dsp_apply_gain:
- @ input: r0 = count, r1 = data, r2 = buf[]
- stmfd sp!, { r4-r8, lr }
-
- ldr r3, [r1, #4] @ r3 = data->num_channels
- ldr r4, [r1, #32] @ r5 = data->gain
-
-.dag_outerloop:
- ldr r1, [r2], #4 @ r1 = buf[0] and increment index of buf[]
- subs r12, r0, #1 @ r12 = r0 = count - 1
- beq .dag_singlesample @ Zero? Only one sample!
-
-.dag_innerloop:
- ldmia r1, { r5, r6 } @ load r5, r6 from r1
- smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8)
- smull r14, r5, r6, r4 @ r14 = FRACMUL_SHL(r6, r4, 8)
- subs r12, r12, #2
- mov r7, r7, lsr #23
- mov r14, r14, lsr #23
- orr r7, r7, r8, asl #9
- orr r14, r14, r5, asl #9
- stmia r1!, { r7, r14 } @ save r7, r14 to [r1] and increment r1
- bgt .dag_innerloop @ end of inner loop
-
- blt .dag_evencount @ < 0? even count
-
-.dag_singlesample:
- ldr r5, [r1] @ handle odd sample
- smull r7, r8, r5, r4 @ r7 = FRACMUL_SHL(r5, r4, 8)
- mov r7, r7, lsr #23
- orr r7, r7, r8, asl #9
- str r7, [r1]
-
-.dag_evencount:
- subs r3, r3, #1
- bgt .dag_outerloop @ end of outer loop
-
- ldmpc regs=r4-r8
- .size dsp_apply_gain, .-dsp_apply_gain
diff --git a/lib/rbcodec/dsp/dsp_arm_v6.S b/lib/rbcodec/dsp/dsp_arm_v6.S
index a9a88ce5bf..a36760f744 100644
--- a/lib/rbcodec/dsp/dsp_arm_v6.S
+++ b/lib/rbcodec/dsp/dsp_arm_v6.S
@@ -20,19 +20,21 @@
****************************************************************************/
/****************************************************************************
- * void sample_output_mono(int count, struct dsp_data *data,
- * const int32_t *src[], int16_t *dst)
+ * void sample_output_mono(struct sample_io_data *this,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
*/
- .section .text, "ax", %progbits
- .align 2
+ .section .text
.global sample_output_mono
.type sample_output_mono, %function
sample_output_mono:
- @ input: r0 = count, r1 = data, r2 = src, r3 = dst
+ @ input: r0 = this, r1 = src, r2 = dst
stmfd sp!, { r4, lr } @
@
- ldr r1, [r1] @ r1 = data->output_scale
- ldr r2, [r2] @ r2 = src[0]
+ ldr r0, [r0] @ r0 = this->outcount
+ ldr r3, [r2, #4] @ r3 = dst->p16out
+ ldr r2, [r1, #4] @ r2 = src->p32[0]
+ ldrb r1, [r1, #19] @ r1 = src->format.output_scale
@
mov r4, #1 @ r4 = 1 << (scale - 1)
mov r4, r4, lsl r1 @
@@ -68,19 +70,21 @@ sample_output_mono:
.size sample_output_mono, .-sample_output_mono
/****************************************************************************
- * void sample_output_stereo(int count, struct dsp_data *data,
- * const int32_t *src[], int16_t *dst)
+ * void sample_output_stereo(struct sample_io_data *this,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
*/
- .section .text, "ax", %progbits
- .align 2
+ .section .text
.global sample_output_stereo
.type sample_output_stereo, %function
sample_output_stereo:
- @ input: r0 = count, r1 = data, r2 = src, r3 = dst
+ @ input: r0 = this, r1 = src, r2 = dst
stmfd sp!, { r4-r7, lr } @
@
- ldr r1, [r1] @ r1 = data->output_scale
- ldmia r2, { r2, r4 } @ r2 = src[0], r4 = src[1]
+ ldr r0, [r0] @ r0 = this->outcount
+ ldr r3, [r2, #4] @ r3 = dst->p16out
+ ldmib r1, { r2, r4 } @ r2 = src->p32[0], r4 = src->p32[1]
+ ldrb r1, [r1, #19] @ r1 = src->format.output_scale
@
mov r5, #1 @ r5 = 1 << (scale - 1)
mov r5, r5, lsl r1 @
diff --git a/lib/rbcodec/dsp/dsp_asm.h b/lib/rbcodec/dsp/dsp_asm.h
deleted file mode 100644
index 7bf18370a3..0000000000
--- a/lib/rbcodec/dsp/dsp_asm.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 Thom Johansen
- *
- * 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>
-
-#ifndef _DSP_ASM_H
-#define _DSP_ASM_H
-
-/* Set the appropriate #defines based on CPU or whatever matters */
-#if defined(CPU_ARM)
-#define DSP_HAVE_ASM_APPLY_GAIN
-#define DSP_HAVE_ASM_RESAMPLING
-#define DSP_HAVE_ASM_CROSSFEED
-#define DSP_HAVE_ASM_SOUND_CHAN_MONO
-#define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
-#define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
-#define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
-#define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
-#elif defined (CPU_COLDFIRE)
-#define DSP_HAVE_ASM_APPLY_GAIN
-#define DSP_HAVE_ASM_RESAMPLING
-#define DSP_HAVE_ASM_CROSSFEED
-#define DSP_HAVE_ASM_SOUND_CHAN_MONO
-#define DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
-#define DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
-#define DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
-#define DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
-#endif /* CPU_COLDFIRE */
-
-/* Declare prototypes based upon what's #defined above */
-#ifdef DSP_HAVE_ASM_CROSSFEED
-void apply_crossfeed(int count, int32_t *buf[]);
-#endif
-
-#ifdef DSP_HAVE_ASM_APPLY_GAIN
-void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[]);
-#endif /* DSP_HAVE_ASM_APPLY_GAIN* */
-
-#ifdef DSP_HAVE_ASM_RESAMPLING
-int dsp_upsample(int count, struct dsp_data *data,
- const int32_t *src[], int32_t *dst[]);
-int dsp_downsample(int count, struct dsp_data *data,
- const int32_t *src[], int32_t *dst[]);
-#endif /* DSP_HAVE_ASM_RESAMPLING */
-
-#ifdef DSP_HAVE_ASM_SOUND_CHAN_MONO
-void channels_process_sound_chan_mono(int count, int32_t *buf[]);
-#endif
-
-#ifdef DSP_HAVE_ASM_SOUND_CHAN_CUSTOM
-void channels_process_sound_chan_custom(int count, int32_t *buf[]);
-#endif
-
-#ifdef DSP_HAVE_ASM_SOUND_CHAN_KARAOKE
-void channels_process_sound_chan_karaoke(int count, int32_t *buf[]);
-#endif
-
-#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_STEREO
-void sample_output_stereo(int count, struct dsp_data *data,
- const int32_t *src[], int16_t *dst);
-#endif
-
-#ifdef DSP_HAVE_ASM_SAMPLE_OUTPUT_MONO
-void sample_output_mono(int count, struct dsp_data *data,
- const int32_t *src[], int16_t *dst);
-#endif
-
-#endif /* _DSP_ASM_H */
diff --git a/lib/rbcodec/dsp/dsp_cf.S b/lib/rbcodec/dsp/dsp_cf.S
index 15ec7eb383..c710df5177 100644
--- a/lib/rbcodec/dsp/dsp_cf.S
+++ b/lib/rbcodec/dsp/dsp_cf.S
@@ -19,23 +19,27 @@
* KIND, either express or implied.
*
****************************************************************************/
+#include "config.h"
/****************************************************************************
- * void dsp_apply_gain(int count, struct dsp_data *data, int32_t *buf[])
+ * void pga_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
*/
.section .text
.align 2
- .global dsp_apply_gain
-dsp_apply_gain:
+ .global pga_process
+pga_process:
+ | input: 4(sp) = this, 8(sp) = buf_p
+ movem.l 4(%sp), %a0-%a1 | %a0 = this, %a1 = buf_p
+ move.l (%a0), %a0 | %a0 = this->data = &pga_data
+ move.l (%a0), %a0 | %a0 = data->gain
+ move.l (%a1), %a1 | %a1 = buf = *buf_p
lea.l -20(%sp), %sp | save registers
movem.l %d2-%d4/%a2-%a3, (%sp) |
- movem.l 28(%sp), %a0-%a1 | %a0 = data,
- | %a1 = buf
- move.l 4(%a0), %d1 | %d1 = data->num_channels
- move.l 32(%a0), %a0 | %a0 = data->gain (in s8.23)
+ clr.l %d1 | %d1 = buf->format.num_channels
+ move.b 17(%a1), %d1 |
10: | channel loop |
- move.l 24(%sp), %d0 | %d0 = count
- move.l -4(%a1, %d1.l*4), %a2 | %a2 = s = buf[ch-1]
+ move.l (%a1), %d0 | %d0 = buf->remcount
+ move.l (%a1, %d1.l*4), %a2 | %a2 = s = buf->p32[ch-1]
move.l %a2, %a3 | %a3 = d = s
move.l (%a2)+, %d2 | %d2 = *s++,
mac.l %a0, %d2, (%a2)+, %d2, %acc0 | %acc0 = S(n)*gain, load S(n+1)
@@ -61,25 +65,29 @@ dsp_apply_gain:
movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers
lea.l 20(%sp), %sp | cleanup stack
rts |
- .size dsp_apply_gain,.-dsp_apply_gain
+ .size pga_process, .-pga_process
/****************************************************************************
- * void apply_crossfeed(int count, int32_t *buf[])
+ * void crossfeed_process(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
*/
.section .text
.align 2
- .global apply_crossfeed
-apply_crossfeed:
+ .global crossfeed_process
+crossfeed_process:
+ | input: 4(sp) = this, 8(sp) = buf_p
lea.l -44(%sp), %sp |
movem.l %d2-%d7/%a2-%a6, (%sp) | save all regs
- movem.l 48(%sp), %d7/%a4 | %d7 = count, %a4 = src
- movem.l (%a4), %a4-%a5 | %a4 = src[0], %a5 = src[1]
- lea.l crossfeed_data, %a1 | %a1 = &crossfeed_data
+ movem.l 48(%sp), %a1/%a4 | %a1 = this, %a4 = buf_p
+ move.l (%a4), %a4 | %a4 = buf = *buf_p
+ movem.l (%a4), %d7/%a4-%a5 | %d7 = buf->remcount, %a4 = buf->p32[0],
+ | %a5 = buf->p32[1]
+ move.l (%a1), %a1 | %a1 = &crossfeed_state
move.l (%a1)+, %d6 | %d6 = direct gain
movem.l 12(%a1), %d0-%d3 | fetch filter history samples
- move.l 132(%a1), %a0 | fetch delay line address
+ lea.l 132(%a1), %a6 | %a6 = delay line wrap limit
+ move.l (%a6), %a0 | fetch delay line address
movem.l (%a1), %a1-%a3 | load filter coefs
- lea.l crossfeed_data+136, %a6 | %a6 = delay line wrap limit
bra.b 20f | loop start | go to loop start point
/* Register usage in loop:
* %a0 = delay_p, %a1..%a3 = b0, b1, a1 (filter coefs),
@@ -109,174 +117,181 @@ apply_crossfeed:
mac.l %d6, %d5, %acc1 | %acc1 += gain*x_r[n]
cmp.l %a6, %a0 | wrap %a0 if passed end
bhs.b 30f | wrap buffer |
- .word 0x51fb | tpf.l | trap the buffer wrap
+ tpf.l | trap the buffer wrap
30: | wrap buffer | ...fwd taken branches more costly
- lea.l -104(%a0), %a0 | wrap it up
+ lea.l -104(%a6), %a0 | wrap it up
subq.l #1, %d7 | --count > 0 ?
bgt.b 10b | loop | yes? do more
movclr.l %acc0, %d4 | write last outputs
move.l %d4, (%a4) | .
movclr.l %acc1, %d5 | .
move.l %d5, (%a5) | .
- lea.l crossfeed_data+16, %a1 | save data back to struct
- movem.l %d0-%d3, (%a1) | ...history
- move.l %a0, 120(%a1) | ...delay_p
+ movem.l %d0-%d3, -120(%a6) | ...history
+ move.l %a0, (%a6) | ...delay_p
movem.l (%sp), %d2-%d7/%a2-%a6 | restore all regs
lea.l 44(%sp), %sp |
rts |
- .size apply_crossfeed,.-apply_crossfeed
+ .size crossfeed_process,.-crossfeed_process
/****************************************************************************
- * int dsp_downsample(int count, struct dsp_data *data,
- * in32_t *src[], int32_t *dst[])
+ * int lin_resample_resample(struct resample_data *data,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
*/
.section .text
.align 2
- .global dsp_downsample
-dsp_downsample:
- lea.l -40(%sp), %sp | save non-clobberables
- movem.l %d2-%d7/%a2-%a5, (%sp) |
- movem.l 44(%sp), %d2/%a0-%a2 | %d2 = count
- | %a0 = data
+ .global lin_resample_resample
+lin_resample_resample:
+ | input: 4(sp) = data, 8(sp) = src, 12(sp) = dst
+ lea.l -44(%sp), %sp | save non-volatiles
+ movem.l %d2-%d7/%a2-%a6, (%sp) |
+ movem.l 48(%sp), %a0-%a2 | %a0 = data
| %a1 = src
| %a2 = dst
- movem.l 4(%a0), %d3-%d4 | %d3 = ch = data->num_channels
- | %d4 = delta = data->resample_data.delta
+ clr.l %d1 | %d1 = ch = src->format.num_channels
+ move.b 17(%a1), %d1 |
moveq.l #16, %d7 | %d7 = shift
-10: | channel loop |
- move.l 12(%a0), %d5 | %d5 = phase = data->resample_data.phase
- move.l -4(%a1, %d3.l*4), %a3 | %a3 = s = src[ch-1]
- move.l -4(%a2, %d3.l*4), %a4 | %a4 = d = dst[ch-1]
- lea.l 12(%a0, %d3.l*4), %a5 | %a5 = &data->resample_data.ast_sample[ch-1]
- move.l (%a5), %d0 | %d0 = last = data->resample_data.last_sample[ch-1]
- move.l -4(%a3, %d2.l*4), (%a5) | data->resample_data.last_sample[ch-1] = s[count-1]
- move.l %d5, %d6 | %d6 = pos = phase >> 16
- lsr.l %d7, %d6 |
- cmp.l %d2, %d6 | past end of samples?
- bge.b 40f | skip resample loop| yes? skip loop
- tst.l %d6 | need last sample of prev. frame?
- bne.b 20f | resample loop | no? start main loop
- move.l (%a3, %d6.l*4), %d1 | %d1 = s[pos]
- bra.b 30f | resample start last | start with last (last in %d0)
-20: | resample loop |
- lea.l -4(%a3, %d6.l*4), %a5 | load s[pos-1] and s[pos]
- movem.l (%a5), %d0-%d1 |
-30: | resample start last |
- sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1]
- move.l %d0, %acc0 | %acc0 = previous sample
- move.l %d5, %d0 | frac = (phase << 16) >> 1
+.lrs_channel_loop: |
+ movem.l (%a0), %d2-%d3 | %d2 = delta = data->delta,
+ | %d3 = phase = data->phase
+ move.l (%a1), %d4 | %d4 = srcrem = src->remcount
+ move.l 12(%a2), %d5 | %d5 = dstrem = dst->bufcount
+ cmp.l #0x8000, %d4 | %d4 = MIN(srcrem, 0x8000)
+ ble.b 10f |
+ move.l #0x8000, %d4 |
+10: |
+ move.l (%a1, %d1.l*4), %a3 | %a3 = s = src->p32[ch]
+ move.l (%a2, %d1.l*4), %a4 | %a4 = d = dst->p32[ch]
+ move.l %d3, %d0 | %d0 = pos
+ lsr.l %d7, %d0 | ...
+ beq.b 11f | pos == 0?
+ cmp.l %d4, %d0 | pos = MIN(pos, srcrem)
+ blt.b 12f |
+ move.l %d4, %d0 | pos = srcrem
+ move.l -4(%a3, %d0.l*4), %d6 | %d6 = last = s[pos - 1]
+ bra.w .lrs_channel_complete | at limit; nothing to do but next
+11: |
+ move.l 4(%a0, %d1.l*4), %d6 | %d6 = last = last_sample[ch]
+ tpf.l | trap next move.l (last = s[pos - 1])
+12: |
+ move.l -4(%a3, %d0.l*4), %d6 | %d6 = last = s[pos - 1]
+ cmp.l #0x10000, %d2 | delta >= 1.0?
+ bhs.b .lrs_downsample | yes? downsampling
+ |
+ /** Upsampling **/ |
+ lea.l (%a3, %d0.l*4), %a3 | %a3 = &s[pos]
+ sub.l %d4, %d0 | %d0 = pos - srcrem = -dte
+ lsl.l %d7, %d2 | move delta to bits 30..15
+ lsr.l #1, %d2 |
+ lsl.l %d7, %d3 | move phase to bits 30..15
+ lsr.l #1, %d3 |
+ move.l (%a3)+, %a5 | %a5 = s[pos]
+ move.l %a5, %a6 | %a6 = diff = s[pos] - last
+ sub.l %d6, %a6 |
+ bra.b 22f |
+ /* Funky loop structure is to avoid emac latency stalls */
+20: |
+ move.l (%a3)+, %a5 | %a5 = s[pos]
+ move.l %a5, %a6 | %a6 = diff = s[pos] - last
+ sub.l %d6, %a6 |
+21: |
+ movclr.l %acc0, %d7 | *d++ = %d7 = result
+ move.l %d7, (%a4)+ |
+22: |
+ move.l %d6, %acc0 | %acc0 = last
+ mac.l %d3, %a6, %acc0 | %acc0 += frac * diff
+ subq.l #1, %d5 | dstrem <= 0?
+ ble.b 23f | yes? stop
+ add.l %d2, %d3 | phase += delta
+ bpl.b 21b | load next values?
+ move.l %a5, %d6 |
+ bclr.l #31, %d3 | clear sign bit
+ addq.l #1, %d0 | dte > 0?
+ bmi.b 20b | yes? continue resampling
+ tpf.w | trap next add.l (phase += delta)
+23: |
+ add.l %d2, %d3 | phase += delta
+ lsl.l #1, %d3 | frac -> phase
+ bcs.b 24f | was sign bit set?
+ tpf.l |
+24: |
+ move.l %a5, %d6 | yes? was going to move to new s[pos]
+ addq.l #1, %d0 |
+ movclr.l %acc0, %d7 | *d = %d7 = result
+ move.l %d7, (%a4) |
+ add.l %d4, %d0 | %d0 = -dte + srcrem = pos
+ or.l %d0, %d3 | restore phase
+ swap.w %d3 |
+ moveq.l #16, %d7 | %d7 = shift
+ bra.b .lrs_channel_complete |
+ |
+ /** Downsampling **/ |
+.lrs_downsample: |
+ move.l (%a3, %d0.l*4), %a5 | %a5 = s[pos]
+ bra.b 31f |
+30: |
+ lea.l -4(%a3, %d0.l*4), %a5 | %d6 = s[pos - 1], %a5 = s[pos]
+ movem.l (%a5), %d6/%a5 |
+31: |
+ move.l %d6, %acc0 | %acc0 = last
+ sub.l %d6, %a5 | %a5 = diff = s[pos] - s[pos - 1]
+ move.l %d3, %d0 | frac = (phase << 16) >> 1
lsl.l %d7, %d0 |
lsr.l #1, %d0 |
- mac.l %d0, %d1, %acc0 | %acc0 += frac * diff
- add.l %d4, %d5 | phase += delta
- move.l %d5, %d6 | pos = phase >> 16
- lsr.l %d7, %d6 |
- movclr.l %acc0, %d0 |
- move.l %d0, (%a4)+ | *d++ = %d0
- cmp.l %d2, %d6 | pos < count?
- blt.b 20b | resample loop | yes? continue resampling
-40: | skip resample loop |
- subq.l #1, %d3 | ch > 0?
- bgt.b 10b | channel loop | yes? process next channel
- lsl.l %d7, %d2 | wrap phase to start of next frame
- sub.l %d2, %d5 | data->resample_data.phase =
- move.l %d5, 12(%a0) | ... phase - (count << 16)
- move.l %a4, %d0 | return d - d[0]
- sub.l (%a2), %d0 |
- asr.l #2, %d0 | convert bytes->samples
- movem.l (%sp), %d2-%d7/%a2-%a5 | restore non-clobberables
- lea.l 40(%sp), %sp | cleanup stack
+ mac.l %d0, %a5, %acc0 | %acc0 += frac * diff
+ add.l %d2, %d3 | phase += delta
+ move.l %d3, %d0 | pos = phase >> 16
+ lsr.l %d7, %d0 |
+ movclr.l %acc0, %a5 |
+ move.l %a5, (%a4)+ | *d++ = %d0
+ subq.l #1, %d5 | dst full?
+ ble.b 32f | yes? stop
+ cmp.l %d4, %d0 | pos < srcrem?
+ blt.b 30b | yes? continue resampling
+ tpf.l | trap cmp.l and ble.b
+32: |
+ cmp.l %d4, %d0 | pos = MIN(pos, srcrem)
+ ble.b 33f |
+ move.l %d4, %d0 |
+33: |
+ move.l -4(%a3, %d0.l*4), %d6 | %d6 = s[pos - 1]
+ |
+.lrs_channel_complete: |
+ move.l %d6, 4(%a0, %d1.l*4) | last_sample[ch] = last
+ subq.l #1, %d1 | ch > 0?
+ bgt.w .lrs_channel_loop | yes? process next channel
+ |
+ move.l 12(%a2), %d1 | %d1 = dst->bufcount
+ sub.l %d5, %d1 | written = dst->bufcount - dstrem
+ move.l %d1, (%a2) | dst->remcount = written
+ move.l %d0, %d1 | wrap phase to position in next frame
+ lsl.l %d7, %d1 | data->phase = phase - (pos << 16)
+ sub.l %d1, %d3 | ...
+ move.l %d3, 4(%a0) | ...
+ movem.l (%sp), %d2-%d7/%a2-%a6 | restore non-volatiles
+ lea.l 44(%sp), %sp | cleanup stack
rts | buh-bye
- .size dsp_downsample,.-dsp_downsample
-/****************************************************************************
- * int dsp_upsample(int count, struct dsp_data *dsp,
- * const int32_t *src[], int32_t *dst[])
- */
- .section .text
- .align 2
- .global dsp_upsample
-dsp_upsample:
- lea.l -40(%sp), %sp | save non-clobberables
- movem.l %d2-%d7/%a2-%a5, (%sp) |
- movem.l 44(%sp), %d2/%a0-%a2 | %d2 = count
- | %a0 = data
- | %a1 = src
- | %a2 = dst
- movem.l 4(%a0), %d3-%d4 | %d3 = ch = channels
- | %d4 = delta = data->resample_data.delta
- swap %d4 | swap delta to high word to use...
- | ...carries to increment position
-10: | channel loop |
- move.l 12(%a0), %d5 | %d5 = phase = data->resample_data.phase
- move.l -4(%a1, %d3.l*4), %a3 | %a3 = s = src[ch-1]
- lea.l 12(%a0, %d3.l*4), %a4 | %a4 = &data->resample_data.last_sample[ch-1]
- lea.l -4(%a3, %d2.l*4), %a5 | %a5 = src_end = &src[count-1]
- move.l (%a4), %d0 | %d0 = last = data->resample_data.last_sample[ch-1]
- move.l (%a5), (%a4) | data->resample_data.last_sample[ch-1] = s[count-1]
- move.l -4(%a2, %d3.l*4), %a4 | %a4 = d = dst[ch-1]
- move.l (%a3)+, %d1 | fetch first sample - might throw this...
- | ...away later but we'll be preincremented
- move.l %d1, %d6 | save sample value
- sub.l %d0, %d1 | %d1 = diff = s[0] - last
- swap %d5 | swap phase to high word to use
- | carries to increment position
- move.l %d5, %d7 | %d7 = pos = phase >> 16
- clr.w %d5 |
- eor.l %d5, %d7 | pos == 0?
- beq.b 40f | loop start | yes? start loop
- cmp.l %d2, %d7 | past end of samples?
- bge.b 50f | skip resample loop| yes? go to next channel and collect info
- lea.l (%a3, %d7.l*4), %a3 | %a3 = s = &s[pos+1]
- movem.l -8(%a3), %d0-%d1 | %d0 = s[pos-1], %d1 = s[pos]
- move.l %d1, %d6 | save sample value
- sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1]
- bra.b 40f | loop start |
-20: | next sample loop |
- move.l %d6, %d0 | move previous sample to %d0
- move.l (%a3)+, %d1 | fetch next sample
- move.l %d1, %d6 | save sample value
- sub.l %d0, %d1 | %d1 = diff = s[pos] - s[pos-1]
-30: | same sample loop |
- movclr.l %acc0, %d7 | %d7 = result
- move.l %d7, (%a4)+ | *d++ = %d7
-40: | loop start |
- lsr.l #1, %d5 | make phase into frac
- move.l %d0, %acc0 | %acc0 = s[pos-1]
- mac.l %d1, %d5, %acc0 | %acc0 = diff * frac
- lsl.l #1, %d5 | restore frac to phase
- add.l %d4, %d5 | phase += delta
- bcc.b 30b | same sample loop | load next values?
- cmp.l %a5, %a3 | src <= src_end?
- bls.b 20b | next sample loop | yes? continue resampling
- movclr.l %acc0, %d7 | %d7 = result
- move.l %d7, (%a4)+ | *d++ = %d7
-50: | skip resample loop |
- subq.l #1, %d3 | ch > 0?
- bgt.b 10b | channel loop | yes? process next channel
- swap %d5 | wrap phase to start of next frame
- move.l %d5, 12(%a0) | ...and save in data->resample_data.phase
- move.l %a4, %d0 | return d - d[0]
- sub.l (%a2), %d0 |
- movem.l (%sp), %d2-%d7/%a2-%a5 | restore non-clobberables
- asr.l #2, %d0 | convert bytes->samples
- lea.l 40(%sp), %sp | cleanup stack
- rts | buh-bye
- .size dsp_upsample,.-dsp_upsample
+ .size lin_resample_resample, .-lin_resample_resample
+
/****************************************************************************
- * void channels_process_sound_chan_mono(int count, int32_t *buf[])
+ * void channel_mode_proc_mono(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
*
* Mix left and right channels 50/50 into a center channel.
*/
.section .text
.align 2
- .global channels_process_sound_chan_mono
-channels_process_sound_chan_mono:
- movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf
+ .global channel_mode_proc_mono
+channel_mode_proc_mono:
+ | input: 4(sp) = this, 8(sp) = buf_p
+ move.l 8(%sp), %a0 | %a0 = buf_p
+ move.l (%a0), %a0 | %a0 = buf = *buf_p
lea.l -20(%sp), %sp | save registers
movem.l %d2-%d4/%a2-%a3, (%sp) |
- movem.l (%a0), %a0-%a1 | get channel pointers
+ movem.l (%a0), %d0/%a0-%a1 | %d0 = buf->remcount, %a0 = buf->p32[0],
+ | %a1 = buf->p32[1]
move.l %a0, %a2 | use separate dst pointers since read
move.l %a1, %a3 | pointers run one ahead of write
move.l #0x40000000, %d3 | %d3 = 0.5
@@ -301,26 +316,29 @@ channels_process_sound_chan_mono:
movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers
lea.l 20(%sp), %sp | cleanup
rts |
- .size channels_process_sound_chan_mono, \
- .-channels_process_sound_chan_mono
+ .size channel_mode_proc_mono, .-channel_mode_proc_mono
/****************************************************************************
- * void channels_process_sound_chan_custom(int count, int32_t *buf[])
+ * void channel_mode_proc_custom(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
*
* Apply stereo width (narrowing/expanding) effect.
*/
.section .text
.align 2
- .global channels_process_sound_chan_custom
-channels_process_sound_chan_custom:
- movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf
+ .global channel_mode_proc_custom
+channel_mode_proc_custom:
+ | input: 4(sp) = this, 8(sp) = buf_p
lea.l -28(%sp), %sp | save registers
movem.l %d2-%d6/%a2-%a3, (%sp) |
- movem.l (%a0), %a0-%a1 | get channel pointers
+ movem.l 32(%sp), %a0-%a1 | %a0 = this, %a1 = buf_p
+ move.l (%a1), %a1 | %a1 = buf = *buf_p
+ move.l (%a0), %a2 | %a2 = this->data = &channel_mode_data
+ movem.l (%a1), %d0/%a0-%a1 | %d0 = buf->remcount, %a0 = buf->p32[0],
+ | %a1 = buf->p32[1]
+ movem.l (%a2), %d3-%d4 | %d3 = sw_gain, %d4 = sw_cross
move.l %a0, %a2 | use separate dst pointers since read
move.l %a1, %a3 | pointers run one ahead of write
- move.l dsp_sw_gain, %d3 | load straight (mid) gain
- move.l dsp_sw_cross, %d4 | load cross (side) gain
move.l (%a0)+, %d1 | prime the input registers
move.l (%a1)+, %d2 |
mac.l %d1, %d3 , %acc0 | L = l*gain + r*cross
@@ -348,22 +366,25 @@ channels_process_sound_chan_custom:
movem.l (%sp), %d2-%d6/%a2-%a3 | restore registers
lea.l 28(%sp), %sp | cleanup
rts |
- .size channels_process_sound_chan_custom, \
- .-channels_process_sound_chan_custom
+ .size channel_mode_proc_custom, .-channel_mode_proc_custom
/****************************************************************************
- * void channels_process_sound_chan_karaoke(int count, int32_t *buf[])
+ * void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
+ * struct dsp_buffer **buf_p)
*
* Separate channels into side channels.
*/
.section .text
.align 2
- .global channels_process_sound_chan_karaoke
-channels_process_sound_chan_karaoke:
- movem.l 4(%sp), %d0/%a0 | %d0 = count, %a0 = buf
+ .global channel_mode_proc_karaoke
+channel_mode_proc_karaoke:
+ | input: 4(sp) = this, 8(sp) = buf_p
+ move.l 8(%sp), %a0 | %a0 = buf_p
+ move.l (%a0), %a0 | %a0 = buf = *buf_p
lea.l -20(%sp), %sp | save registers
movem.l %d2-%d4/%a2-%a3, (%sp) |
- movem.l (%a0), %a0-%a1 | get channel src pointers
+ movem.l (%a0), %d0/%a0-%a1 | %d0 = buf->remcount, %a0 = buf->p32[0],
+ | %a1 = buf->p32[1]
move.l %a0, %a2 | use separate dst pointers since read
move.l %a1, %a3 | pointers run one ahead of write
move.l #0x40000000, %d3 | %d3 = 0.5
@@ -390,12 +411,90 @@ channels_process_sound_chan_karaoke:
movem.l (%sp), %d2-%d4/%a2-%a3 | restore registers
lea.l 20(%sp), %sp | cleanup
rts |
- .size channels_process_sound_chan_karaoke, \
- .-channels_process_sound_chan_karaoke
+ .size channel_mode_proc_karaoke, .-channel_mode_proc_karaoke
+
+/****************************************************************************
+ * void filter_process(struct dsp_filter *f, int32_t *buf[], int count,
+ * unsigned int channels)
+ *
+ * define HIGH_PRECISION as '1' to make filtering calculate lower bits after
+ * shifting. without this, "shift" - 1 of the lower bits will be lost here.
+ */
+#define HIGH_PRECISION 0
+ .text
+ .global filter_process
+filter_process:
+ | input: 4(sp) = f, 8(sp) = buf, 12(sp) = count, 16(sp) = channels
+ lea.l -44(%sp), %sp | save clobbered regs
+#if HIGH_PRECISION
+ movem.l %d2-%d7/%a2-%a6, (%sp) | .
+#else
+ movem.l %d2-%d6/%a2-%a6, (%sp) |
+#endif
+ move.l 48(%sp), %a5 | fetch filter structure address
+ clr.l %d6 | load shift count
+ move.b 52(%a5), %d6 | .
+ subq.l #1, %d6 | EMAC gives us one free shift
+#if HIGH_PRECISION
+ moveq.l #8, %d7
+ sub.l %d6, %d7 | shift for lower part of accumulator
+#endif
+ movem.l (%a5), %a0-%a4 | load coefs
+ lea.l 20(%a5), %a5 | point to filter history
+
+10: | channel loop
+ move.l 52(%sp), %a6 | load input channel pointer
+ addq.l #4, 52(%sp) | point x to next channel
+ move.l (%a6), %a6 |
+ move.l 56(%sp), %d5 | number of samples
+ movem.l (%a5), %d0-%d3 | load filter history
+
+ | d0-d3 = history, d4 = temp, d5 = sample count, d6 = upper shift amount,
+ | d7 = lower shift amount,a0-a4 = coefs, a5 = history pointer, a6 = buf[ch]
+20: | loop
+ | Direct form 1 filtering code. We assume DSP has put EMAC in frac mode.
+ | y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
+ | where y[] is output and x[] is input. This is performed out of order
+ | to do parallel load of input value.
+ mac.l %a2, %d1, %acc0 | acc = b2*x[i - 2]
+ move.l %d0, %d1 | fix input history
+ mac.l %a1, %d0, (%a6), %d0, %acc0 | acc += b1*x[i - 1], x[i] -> d0
+ mac.l %a0, %d0, %acc0 | acc += b0*x[i]
+ mac.l %a3, %d2, %acc0 | acc += a1*y[i - 1]
+ mac.l %a4, %d3, %acc0 | acc += a2*y[i - 2]
+ move.l %d2, %d3 | fix output history
+#if HIGH_PRECISION
+ move.l %accext01, %d2 | fetch lower part of accumulator
+ move.b %d2, %d4 | clear upper three bytes
+ lsr.l %d7, %d4 | shift lower bits
+#endif
+ movclr.l %acc0, %d2 | fetch upper part of result
+ asl.l %d6, %d2 | restore fixed point format
+#if HIGH_PRECISION
+ or.l %d2, %d4 | combine lower and upper parts
+#endif
+ move.l %d2, (%a6)+ | save result
+ subq.l #1, %d5 | are we done with this channel?
+ bgt 20b | loop
+
+ movem.l %d0-%d3, (%a5) | save history back to struct
+ lea.l 16(%a5), %a5 | point to next channel's history
+ subq.l #1, 60(%sp) | have we processed both channels?
+ bhi 10b | channel loop
+
+#if HIGH_PRECISION
+ movem.l (%sp), %d2-%d7/%a2-%a6
+#else
+ movem.l (%sp), %d2-%d6/%a2-%a6
+#endif
+ lea.l 44(%sp), %sp
+ rts
+ .size filter_process, .-filter_process
/****************************************************************************
- * void sample_output_stereo(int count, struct dsp_data *data,
- * const int32_t *src[], int16_t *dst)
+ * void sample_output_stereo(struct sample_io_data *this,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
*
* Framework based on the ubiquitous Rockbox line transfer logic for
* Coldfire CPUs.
@@ -417,20 +516,24 @@ channels_process_sound_chan_karaoke:
.align 2
.global sample_output_stereo
sample_output_stereo:
+ | input: 4(sp) = count, 8(sp) = src, 12(sp) = dst
lea.l -48(%sp), %sp | save registers
move.l %macsr, %d1 | do it now as at many lines will
movem.l %d1-%d7/%a2-%a6, (%sp) | be the far more common condition
move.l #0x80, %macsr | put emac unit in signed int mode
- movem.l 52(%sp), %a0-%a2/%a4 |
- lea.l (%a4, %a0.l*4), %a0 | %a0 = end address
- move.l (%a1), %d1 | %a1 = multiplier: (1 << (16 - scale))
+ movem.l 52(%sp), %a0-%a2 | %a0 = this, %a1 = src, %a2 = dst
+ move.l (%a0), %a0 | %a0 = this->outcount
+ move.l 4(%a2), %a4 | %a4 = dst->p16out
+ lea.l (%a4, %a0.l*4), %a0 | %a0 = count -> end address
+ movem.l 4(%a1), %a2-%a3 | %a2 = src->p32[0], %a3 = src->p32[1]
+ clr.l %d1 | %a1 = multiplier: (1 << (16 - scale))
+ move.b 19(%a1), %d1 | %d1 = src->format.output_scale
sub.l #16, %d1 |
neg.l %d1 |
moveq.l #1, %d0 |
asl.l %d1, %d0 |
move.l %d0, %a1 |
move.l #0x8000, %a6 | %a6 = rounding term
- movem.l (%a2), %a2-%a3 | get L/R channel pointers
moveq.l #28, %d0 | %d0 = second line bound
add.l %a4, %d0 |
and.l #0xfffffff0, %d0 |
@@ -447,7 +550,7 @@ sample_output_stereo:
mac.l %d2, %a1, %acc1 | shift R to high word
movclr.l %acc0, %d1 | get possibly saturated results
movclr.l %acc1, %d2 |
- swap %d2 | move R to low word
+ swap.w %d2 | move R to low word
move.w %d2, %d1 | interleave MS 16 bits of each
move.l %d1, (%a4)+ | ...and write both
cmp.l %a4, %d0 |
@@ -477,10 +580,10 @@ sample_output_stereo:
mac.l %d1, %a1, (%a2)+, %d2, %acc1 | with saturation
mac.l %d2, %a1, (%a2)+, %d3, %acc2 |
mac.l %d3, %a1 , %acc3 |
- swap %d4 | a) interleave most significant...
- swap %d5 |
- swap %d6 |
- swap %d7 |
+ swap.w %d4 | a) interleave most significant...
+ swap.w %d5 |
+ swap.w %d6 |
+ swap.w %d7 |
movclr.l %acc0, %d0 | obtain L results
movclr.l %acc1, %d1 |
movclr.l %acc2, %d2 |
@@ -503,7 +606,7 @@ sample_output_stereo:
mac.l %d2, %a1, %acc1 |
movclr.l %acc0, %d1 |
movclr.l %acc1, %d2 |
- swap %d2 |
+ swap.w %d2 |
move.w %d2, %d1 |
move.l %d1, (%a4)+ |
cmp.l %a4, %a0 |
@@ -516,8 +619,9 @@ sample_output_stereo:
.size sample_output_stereo, .-sample_output_stereo
/****************************************************************************
- * void sample_output_mono(int count, struct dsp_data *data,
- * const int32_t *src[], int16_t *dst)
+ * void sample_output_mono(struct sample_io_data *this,
+ * struct dsp_buffer *src,
+ * struct dsp_buffer *dst)
*
* Same treatment as sample_output_stereo but for one channel.
*/
@@ -525,19 +629,23 @@ sample_output_stereo:
.align 2
.global sample_output_mono
sample_output_mono:
+ | input: 4(sp) = count, 8(sp) = src, 12(sp) = dst
lea.l -32(%sp), %sp | save registers
move.l %macsr, %d1 | do it now as at many lines will
movem.l %d1-%d5/%a2-%a4, (%sp) | be the far more common condition
move.l #0x80, %macsr | put emac unit in signed int mode
- movem.l 36(%sp), %a0-%a3 |
- lea.l (%a3, %a0.l*4), %a0 | %a0 = end address
- move.l (%a1), %d1 | %d5 = multiplier: (1 << (16 - scale))
+ movem.l 36(%sp), %a0-%a2 | %a0 = this, %a1 = src, %a2 = dst
+ move.l (%a0), %a0 | %a0 = this->outcount
+ move.l 4(%a2), %a3 | %a3 = dst->p16out
+ movem.l 4(%a1), %a2 | %a2 = src->p32[0]
+ lea.l (%a3, %a0.l*4), %a0 | %a0 = count -> end address
+ clr.l %d1 | %d5 = multiplier: (1 << (16 - scale))
+ move.b 19(%a1), %d1 | %d1 = src->format.output_scale
sub.l #16, %d1 |
neg.l %d1 |
moveq.l #1, %d5 |
asl.l %d1, %d5 |
move.l #0x8000, %a4 | %a4 = rounding term
- movem.l (%a2), %a2 | get source channel pointer
moveq.l #28, %d0 | %d0 = second line bound
add.l %a3, %d0 |
and.l #0xfffffff0, %d0 |
@@ -552,7 +660,7 @@ sample_output_mono:
mac.l %d1, %d5, %acc0 | shift L to high word
movclr.l %acc0, %d1 | get possibly saturated results
move.l %d1, %d2 |
- swap %d2 | move R to low word
+ swap.w %d2 | move R to low word
move.w %d2, %d1 | duplicate single channel into
move.l %d1, (%a3)+ | L and R
cmp.l %a3, %d0 |
@@ -575,16 +683,16 @@ sample_output_mono:
movclr.l %acc2, %d2 |
movclr.l %acc3, %d3 |
move.l %d0, %d4 | duplicate single channel
- swap %d4 | into L and R
+ swap.w %d4 | into L and R
move.w %d4, %d0 |
move.l %d1, %d4 |
- swap %d4 |
+ swap.w %d4 |
move.w %d4, %d1 |
move.l %d2, %d4 |
- swap %d4 |
+ swap.w %d4 |
move.w %d4, %d2 |
move.l %d3, %d4 |
- swap %d4 |
+ swap.w %d4 |
move.w %d4, %d3 |
movem.l %d0-%d3, -16(%a3) | write four stereo samples
cmp.l %a3, %a1 |
@@ -598,7 +706,7 @@ sample_output_mono:
mac.l %d1, %d5, %acc0 | the same way as leading ones
movclr.l %acc0, %d1 |
move.l %d1, %d2 |
- swap %d2 |
+ swap.w %d2 |
move.w %d2, %d1 |
move.l %d1, (%a3)+ |
cmp.l %a3, %a0 |
diff --git a/lib/rbcodec/dsp/dsp_core.c b/lib/rbcodec/dsp/dsp_core.c
new file mode 100644
index 0000000000..84fe64adb0
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_core.c
@@ -0,0 +1,554 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ * 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 "dsp.h"
+#include "dsp_sample_io.h"
+#include <sys/types.h>
+
+/* Define LOGF_ENABLE to enable logf output in this file */
+/*#define LOGF_ENABLE*/
+#include "logf.h"
+
+/* Actually generate the database of stages */
+#define DSP_PROC_DB_CREATE
+#include "dsp_proc_entry.h"
+
+/* Linked lists give fewer loads in processing loop compared to some index
+ * list, which is more important than keeping occasionally executed code
+ * simple */
+
+struct dsp_config
+{
+ /** General DSP-local data **/
+ struct sample_io_data io_data; /* Sample input-output data (first) */
+ uint32_t slot_free_mask; /* Mask of free slots for this DSP */
+ uint32_t proc_masks[2]; /* Mask of active/enabled stages */
+ struct dsp_proc_slot
+ {
+ struct dsp_proc_entry proc_entry; /* This enabled stage */
+ struct dsp_proc_slot *next[2]; /* [0]=active next, [1]=enabled next */
+ const struct dsp_proc_db_entry *db_entry;
+ } *proc_slots[2]; /* Pointer to first in list of
+ active/enabled stages */
+
+ /** Misc. extra stuff **/
+#ifdef CPU_COLDFIRE
+ unsigned long old_macsr; /* Old macsr value to restore */
+#endif
+#if 0 /* Not needed now but enable if something must know this */
+ bool processing; /* DSP is processing (to thwart inopportune
+ buffer moves) */
+#endif
+};
+
+/* Pool of slots for stages - supports 32 or fewer combined as-is atm. */
+static struct dsp_proc_slot
+dsp_proc_slot_arr[DSP_NUM_PROC_STAGES+DSP_VOICE_NUM_PROC_STAGES] IBSS_ATTR;
+
+/* General DSP config */
+static struct dsp_config dsp_conf[DSP_COUNT] IBSS_ATTR;
+
+/** Processing stages support functions **/
+
+/* Find the slot for a given enabled id */
+static struct dsp_proc_slot * find_proc_slot(struct dsp_config *dsp,
+ unsigned int id)
+{
+ const uint32_t mask = BIT_N(id);
+
+ if ((dsp->proc_masks[1] & mask) == 0)
+ return NULL; /* Not enabled */
+
+ struct dsp_proc_slot *s = dsp->proc_slots[1];
+
+ while (1) /* In proc_masks == it must be there */
+ {
+ if (BIT_N(s->db_entry->id) == mask)
+ return s;
+
+ s = s->next[1];
+ }
+}
+
+/* Broadcast to all enabled stages or to the one with the specifically
+ * crafted setting */
+static intptr_t proc_broadcast(struct dsp_config *dsp, unsigned int setting,
+ intptr_t value)
+{
+ bool multi = setting < DSP_PROC_SETTING;
+ struct dsp_proc_slot *s = multi ?
+ dsp->proc_slots[1] : find_proc_slot(dsp, setting - DSP_PROC_SETTING);
+
+ while (s != NULL)
+ {
+ intptr_t ret = s->db_entry->configure(&s->proc_entry, dsp, setting,
+ value);
+ if (!multi)
+ return ret;
+
+ s = s->next[1];
+ }
+
+ return multi ? 1 : 0;
+}
+
+/* Generic handler for this->process[0] */
+static void dsp_process_null(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ (void)this; (void)buf_p;
+}
+
+/* Generic handler for this->process[1] */
+static void dsp_format_change_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ enum dsp_proc_ids id =
+ TYPE_FROM_MEMBER(struct dsp_proc_slot, this, proc_entry)->db_entry->id;
+
+ DSP_PRINT_FORMAT(<Default Handler>, id, (*buf_p)->format);
+
+ /* We don't keep back references to the DSP, so just search for it */
+ struct dsp_config *dsp;
+ for (int i = 0; (dsp = dsp_get_config(i)); i++)
+ {
+ struct dsp_proc_slot *slot = find_proc_slot(dsp, id);
+ /* Found one with the id, check if it's this one */
+ if (&slot->proc_entry == this && dsp_proc_active(dsp, id))
+ {
+ dsp_proc_call(this, buf_p, 0);
+ break;
+ }
+ }
+}
+
+/* Add an item to the enabled list */
+static struct dsp_proc_slot *
+dsp_proc_enable_enlink(struct dsp_config *dsp, uint32_t mask)
+{
+ /* Use the lowest-indexed available slot */
+ int slot = find_first_set_bit(dsp->slot_free_mask);
+
+ if (slot == 32)
+ {
+ /* Should NOT happen, ever, unless called before init */
+ DEBUGF("DSP %d: no slots!\n", (int)dsp_get_id(dsp));
+ return NULL;
+ }
+
+ const struct dsp_proc_db_entry *db_entry_prev = NULL;
+ const struct dsp_proc_db_entry *db_entry;
+
+ /* Order of enabled list is same as DB array */
+ for (unsigned int i = 0;; i++)
+ {
+ if (i >= DSP_NUM_PROC_STAGES)
+ return NULL;
+
+ db_entry = dsp_proc_database[i];
+
+ uint32_t m = BIT_N(db_entry->id);
+
+ if (m == mask)
+ break; /* This is the one */
+
+ if (dsp->proc_masks[1] & m)
+ db_entry_prev = db_entry;
+ }
+
+ struct dsp_proc_slot *s = &dsp_proc_slot_arr[slot];
+
+ if (db_entry_prev != NULL)
+ {
+ struct dsp_proc_slot *prev = find_proc_slot(dsp, db_entry_prev->id);
+ s->next[0] = prev->next[0];
+ s->next[1] = prev->next[1];
+ prev->next[1] = s;
+ }
+ else
+ {
+ s->next[0] = dsp->proc_slots[0];
+ s->next[1] = dsp->proc_slots[1];
+ dsp->proc_slots[1] = s;
+ }
+
+ s->db_entry = db_entry; /* record DB entry */
+ dsp->proc_masks[1] |= mask;
+ dsp->slot_free_mask &= ~BIT_N(slot);
+
+ return s;
+}
+
+/* Remove an item from the enabled list */
+static struct dsp_proc_slot *
+dsp_proc_enable_delink(struct dsp_config *dsp, uint32_t mask)
+{
+ struct dsp_proc_slot *s = dsp->proc_slots[1];
+ struct dsp_proc_slot *prev = NULL;
+
+ while (1) /* In proc_masks == it must be there */
+ {
+ if (BIT_N(s->db_entry->id) == mask)
+ {
+ if (prev)
+ prev->next[1] = s->next[1];
+ else
+ dsp->proc_slots[1] = s->next[1];
+
+ dsp->proc_masks[1] &= ~mask;
+ dsp->slot_free_mask |= BIT_N(s - dsp_proc_slot_arr);
+ return s;
+ }
+
+ prev = s;
+ s = s->next[1];
+ }
+}
+
+void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id,
+ bool enable)
+{
+ uint32_t mask = BIT_N(id);
+ bool enabled = dsp->proc_masks[1] & mask;
+
+ if (enable)
+ {
+ /* If enabled, just find it in list, if not, link a new one */
+ struct dsp_proc_slot *s = enabled ? find_proc_slot(dsp, id) :
+ dsp_proc_enable_enlink(dsp, mask);
+
+ if (s == NULL)
+ {
+ DEBUGF("DSP- proc id not valid: %d\n", (int)id);
+ return;
+ }
+
+ if (!enabled)
+ {
+ /* New entry - set defaults */
+ s->proc_entry.data = 0;
+ s->proc_entry.ip_mask = mask;
+ s->proc_entry.process[0] = dsp_process_null;
+ s->proc_entry.process[1] = dsp_format_change_process;
+ }
+
+ enabled = s->db_entry->configure(&s->proc_entry, dsp, DSP_PROC_INIT,
+ enabled) >= 0;
+ if (enabled)
+ return;
+
+ DEBUGF("DSP- proc init failed: %d\n", (int)id);
+ /* Cleanup below */
+ }
+ else if (!enabled)
+ {
+ return; /* No change */
+ }
+
+ dsp_proc_activate(dsp, id, false); /* Deactivate it first */
+ struct dsp_proc_slot *s = dsp_proc_enable_delink(dsp, mask);
+ s->db_entry->configure(&s->proc_entry, dsp, DSP_PROC_CLOSE, 0);
+}
+
+/* Maintain the list structure for the active list where each enabled entry
+ * has a link to the next active item, even if not active which facilitates
+ * switching out of format change mode by a stage during a format change.
+ * When that happens, the iterator must jump over inactive but enabled
+ * stages after its current position. */
+static struct dsp_proc_slot *
+dsp_proc_activate_link(struct dsp_config *dsp, uint32_t mask,
+ struct dsp_proc_slot *s)
+{
+ uint32_t m = BIT_N(s->db_entry->id);
+ uint32_t mor = m | mask;
+
+ if (mor == m) /* Only if same single bit in common */
+ {
+ dsp->proc_masks[0] |= mask;
+ return s;
+ }
+ else if (~mor == 0) /* Only if bits complement */
+ {
+ dsp->proc_masks[0] &= mask;
+ return s->next[0];
+ }
+
+ struct dsp_proc_slot *next = s->next[1];
+ next = dsp_proc_activate_link(dsp, mask, next);
+
+ s->next[0] = next;
+
+ return (m & dsp->proc_masks[0]) ? s : next;
+}
+
+/* Activate or deactivate a stage */
+void dsp_proc_activate(struct dsp_config *dsp, enum dsp_proc_ids id,
+ bool activate)
+{
+ const uint32_t mask = BIT_N(id);
+
+ if (!(dsp->proc_masks[1] & mask))
+ return; /* Not enabled */
+
+ if (activate != !(dsp->proc_masks[0] & mask))
+ return; /* No change in state */
+
+ /* Send mask bit if activating and ones complement if deactivating */
+ dsp->proc_slots[0] = dsp_proc_activate_link(
+ dsp, activate ? mask : ~mask, dsp->proc_slots[1]);
+}
+
+/* Is the stage specified by the id currently active? */
+bool dsp_proc_active(struct dsp_config *dsp, enum dsp_proc_ids id)
+{
+ return (dsp->proc_masks[0] & BIT_N(id)) != 0;
+}
+
+/* Determine by the rules if the processing function should be called */
+static FORCE_INLINE bool dsp_proc_should_call(struct dsp_proc_entry *this,
+ struct dsp_buffer *buf,
+ unsigned int fmt)
+{
+ uint32_t ip_mask = this->ip_mask;
+
+ return UNLIKELY(fmt != 0) || /* Also pass override value */
+ ip_mask == 0 || /* Not in-place */
+ ((ip_mask & buf->proc_mask) == 0 &&
+ (buf->proc_mask |= ip_mask, buf->remcount > 0));
+}
+
+/* Call this->process[fmt] according to the rules (for external call) */
+bool dsp_proc_call(struct dsp_proc_entry *this, struct dsp_buffer **buf_p,
+ unsigned int fmt)
+{
+ if (dsp_proc_should_call(this, *buf_p, fmt))
+ {
+ this->process[fmt == (0u-1u) ? 0 : fmt](this, buf_p);
+ return true;
+ }
+
+ return false;
+}
+
+static inline void dsp_process_start(struct dsp_config *dsp)
+{
+#if defined(CPU_COLDFIRE)
+ /* set emac unit for dsp processing, and save old macsr, we're running in
+ codec thread context at this point, so can't clobber it */
+ dsp->old_macsr = coldfire_get_macsr();
+ coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE);
+#endif
+#if 0 /* Not needed now but enable if something must know this */
+ dsp->processing = true;
+#endif
+ (void)dsp;
+}
+
+static inline void dsp_process_end(struct dsp_config *dsp)
+{
+#if 0 /* Not needed now but enable if something must know this */
+ dsp->processing = false;
+#endif
+#if defined(CPU_COLDFIRE)
+ /* set old macsr again */
+ coldfire_set_macsr(dsp->old_macsr);
+#endif
+ (void)dsp;
+}
+
+/**
+ * dsp_process:
+ *
+ * Process and convert src audio to dst based on the DSP configuration.
+ * dsp: the DSP instance in use
+ *
+ * src:
+ * remcount = number of input samples remaining; set to desired
+ * number of samples to be processed
+ * pin[0] = left channel if non-interleaved, audio data if
+ * interleaved or mono
+ * pin[1] = right channel if non-interleaved, ignored if
+ * interleaved or mono
+ * proc_mask = set to zero on first call, updated by this function
+ * to keep track of which in-place stages have been
+ * run on the buffers to avoid multiple applications of
+ * them
+ * format = for internal buffers, gives the relevant format
+ * details
+ *
+ * dst:
+ * remcount = number of samples placed in buffer so far; set to
+ * zero on first call
+ * p16out = current fill pointer in destination buffer; set to
+ * buffer start on first call
+ * bufcount = remaining buffer space in samples; set to maximum
+ * desired output count on first call
+ * format = ignored
+ *
+ * Processing stops when src is exhausted or dst is filled, whichever
+ * happens first. Samples can still be output when src buffer is empty
+ * if samples are held internally. Generally speaking, continue calling
+ * until no data is consumed and no data is produced to purge the DSP
+ * to the maximum extent feasible. Some internal processing stages may
+ * require more input before more output can be generated, thus there
+ * is no guarantee the DSP is free of data awaiting processing at that
+ * point.
+ *
+ * Additionally, samples consumed and samples produced do not necessarily
+ * have a direct correlation. Samples may be consumed without producing
+ * any output and samples may be produced without consuming any input.
+ * It depends on which stages are actively processing data at the time
+ * of the call and how they function internally.
+ */
+void dsp_process(struct dsp_config *dsp, struct dsp_buffer *src,
+ struct dsp_buffer *dst)
+{
+ if (dst->bufcount <= 0)
+ {
+ /* No place to put anything thus nothing may be safely consumed */
+ return;
+ }
+
+ /* At least perform one yield before starting */
+ long last_yield = current_tick;
+ yield();
+
+ dsp_process_start(dsp);
+
+ /* Tag input with codec-specified sample format */
+ src->format = dsp->io_data.format;
+
+ while (1)
+ {
+ /* Out-of-place-processing stages take the current buf as input
+ * and switch the buffer to their own output buffer */
+ struct dsp_buffer *buf = src;
+ unsigned int fmt = buf->format.changed;
+
+ /* Convert input samples to internal format */
+ dsp->io_data.input_samples[fmt](&dsp->io_data, &buf);
+ fmt = buf->format.changed;
+
+ struct dsp_proc_slot *s = dsp->proc_slots[fmt];
+
+ /* Call all active/enabled stages depending if format is
+ same/changed on the last output buffer */
+ while (s != NULL)
+ {
+ if (dsp_proc_should_call(&s->proc_entry, buf, fmt))
+ {
+ s->proc_entry.process[fmt](&s->proc_entry, &buf);
+ fmt = buf->format.changed;
+ }
+
+ /* The buffer may have changed along with the format flag */
+ s = s->next[fmt];
+ }
+
+ /* Don't overread/write src/destination */
+ int outcount = MIN(dst->bufcount, buf->remcount);
+
+ if (fmt == 0 && outcount <= 0)
+ break; /* Output full or purged internal buffers */
+
+ dsp->io_data.outcount = outcount;
+ dsp->io_data.output_samples[fmt](&dsp->io_data, buf, dst);
+
+ /* Advance buffers by what output consumed and produced */
+ dsp_advance_buffer32(buf, outcount);
+ dsp_advance_buffer_output(dst, outcount);
+
+ /* Yield at least once each tick */
+ long tick = current_tick;
+ if (TIME_AFTER(tick, last_yield))
+ {
+ last_yield = tick;
+ yield();
+ }
+ } /* while */
+
+ dsp_process_end(dsp);
+}
+
+intptr_t dsp_configure(struct dsp_config *dsp, unsigned int setting,
+ intptr_t value)
+{
+ dsp_sample_io_configure(&dsp->io_data, setting, value);
+ return proc_broadcast(dsp, setting, value);
+}
+
+struct dsp_config * dsp_get_config(enum dsp_ids id)
+{
+ if (id >= DSP_COUNT)
+ return NULL;
+
+ return &dsp_conf[id];
+}
+
+/* Return the id given a dsp pointer (or even via something within
+ the struct itself) */
+enum dsp_ids dsp_get_id(const struct dsp_config *dsp)
+{
+ ptrdiff_t id = dsp - dsp_conf;
+
+ if (id < 0 || id >= DSP_COUNT)
+ return DSP_COUNT; /* obviously invalid */
+
+ return (enum dsp_ids)id;
+}
+
+#if 0 /* Not needed now but enable if something must know this */
+bool dsp_is_busy(const struct dsp_config *dsp)
+{
+ return dsp->processing;
+}
+#endif /* 0 */
+
+/* Do what needs initializing before enable/disable calls can be made.
+ * Must be done before changing settings for the first time. */
+void INIT_ATTR dsp_init(void)
+{
+ static const uint8_t slot_count[DSP_COUNT] /* INITDATA_ATTR */ =
+ {
+ [CODEC_IDX_AUDIO] = DSP_NUM_PROC_STAGES,
+ [CODEC_IDX_VOICE] = DSP_VOICE_NUM_PROC_STAGES
+ };
+
+ for (unsigned int i = 0, count, shift = 0;
+ i < DSP_COUNT;
+ i++, shift += count)
+ {
+ struct dsp_config *dsp = &dsp_conf[i];
+
+ count = slot_count[i];
+ dsp->slot_free_mask = MASK_N(uint32_t, count, shift);
+
+ dsp_sample_io_configure(&dsp->io_data, DSP_INIT, i);
+
+ /* Notify each db entry of global init for each DSP */
+ for (unsigned int j = 0; j < DSP_NUM_PROC_STAGES; j++)
+ dsp_proc_database[j]->configure(NULL, dsp, DSP_INIT, i);
+
+ dsp_configure(dsp, DSP_RESET, 0);
+ }
+}
diff --git a/lib/rbcodec/dsp/dsp_filter.c b/lib/rbcodec/dsp/dsp_filter.c
new file mode 100644
index 0000000000..ee0ce1b18f
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_filter.c
@@ -0,0 +1,306 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Thom Johansen
+ *
+ * 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 <stdbool.h>
+#include <string.h>
+#include "config.h"
+#include "fixedpoint.h"
+#include "fracmul.h"
+#include "dsp_filter.h"
+#include "replaygain.h"
+
+enum filter_shift
+{
+ FILTER_BISHELF_SHIFT = 5, /* For bishelf (bass/treble) */
+ FILTER_PEAK_SHIFT = 4, /* Each peaking filter */
+ FILTER_SHELF_SHIFT = 6, /* Each high/low shelving filter */
+};
+
+/**
+ * Calculate first order shelving filter. Filter is not directly usable by the
+ * filter_process() function.
+ * @param cutoff shelf midpoint frequency. See eq_pk_coefs for format.
+ * @param A decibel value multiplied by ten, describing gain/attenuation of
+ * shelf. Max value is 24 dB.
+ * @param low true for low-shelf filter, false for high-shelf filter.
+ * @param c pointer to coefficient storage. Coefficients are s4.27 format.
+ */
+void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c)
+{
+ long sin, cos;
+ int32_t b0, b1, a0, a1; /* s3.28 */
+ const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */
+
+ sin = fp_sincos(cutoff/2, &cos);
+ if (low) {
+ const int32_t sin_div_g = fp_div(sin, g, 25);
+ const int32_t sin_g = FRACMUL(sin, g);
+ cos >>= 3;
+ b0 = sin_g + cos; /* 0.25 .. 4.10 */
+ b1 = sin_g - cos; /* -1 .. 3.98 */
+ a0 = sin_div_g + cos; /* 0.25 .. 4.10 */
+ a1 = sin_div_g - cos; /* -1 .. 3.98 */
+ } else {
+ const int32_t cos_div_g = fp_div(cos, g, 25);
+ const int32_t cos_g = FRACMUL(cos, g);
+ sin >>= 3;
+ b0 = sin + cos_g; /* 0.25 .. 4.10 */
+ b1 = sin - cos_g; /* -3.98 .. 1 */
+ a0 = sin + cos_div_g; /* 0.25 .. 4.10 */
+ a1 = sin - cos_div_g; /* -3.98 .. 1 */
+ }
+
+ const int32_t rcp_a0 = fp_div(1, a0, 57); /* 0.24 .. 3.98, s2.29 */
+ *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */
+ *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */
+ *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */
+}
+
+#ifdef HAVE_SW_TONE_CONTROLS
+/**
+ * Calculate second order section filter consisting of one low-shelf and one
+ * high-shelf section.
+ * @param cutoff_low low-shelf midpoint frequency. See filter_pk_coefs for format.
+ * @param cutoff_high high-shelf midpoint frequency.
+ * @param A_low decibel value multiplied by ten, describing gain/attenuation of
+ * low-shelf part. Max value is 24 dB.
+ * @param A_high decibel value multiplied by ten, describing gain/attenuation of
+ * high-shelf part. Max value is 24 dB.
+ * @param A decibel value multiplied by ten, describing additional overall gain.
+ * @param c pointer to coefficient storage. Coefficients are s4.27 format.
+ */
+void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
+ long A_low, long A_high, long A,
+ struct dsp_filter *f)
+{
+ const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */
+ int32_t c_ls[3], c_hs[3];
+
+ filter_shelf_coefs(cutoff_low, A_low, true, c_ls);
+ filter_shelf_coefs(cutoff_high, A_high, false, c_hs);
+ c_ls[0] = FRACMUL(g, c_ls[0]);
+ c_ls[1] = FRACMUL(g, c_ls[1]);
+
+ /* now we cascade the two first order filters to one second order filter
+ * which can be used by filter_process(). these resulting coefficients have a
+ * really wide numerical range, so we use a fixed point format which will
+ * work for the selected cutoff frequencies (in tone_controls.c) only.
+ */
+ const int32_t b0 = c_ls[0], b1 = c_ls[1], b2 = c_hs[0], b3 = c_hs[1];
+ const int32_t a0 = c_ls[2], a1 = c_hs[2];
+
+ int32_t *c = f->coefs;
+ *c++ = FRACMUL_SHL(b0, b2, 4);
+ *c++ = FRACMUL_SHL(b0, b3, 4) + FRACMUL_SHL(b1, b2, 4);
+ *c++ = FRACMUL_SHL(b1, b3, 4);
+ *c++ = a0 + a1;
+ *c = -FRACMUL_SHL(a0, a1, 4);
+
+ f->shift = FILTER_BISHELF_SHIFT;
+}
+#endif /* HAVE_SW_TONE_CONTROLS */
+
+/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
+ * Slightly faster calculation can be done by deriving forms which use tan()
+ * instead of cos() and sin(), but the latter are far easier to use when doing
+ * fixed point math, and performance is not a big point in the calculation part.
+ * All the 'a' filter coefficients are negated so we can use only additions
+ * in the filtering equation.
+ */
+
+/**
+ * Calculate second order section peaking filter coefficients.
+ * @param cutoff a value from 0 to 0x80000000, where 0 represents 0 Hz and
+ * 0x80000000 represents the Nyquist frequency (samplerate/2).
+ * @param Q Q factor value multiplied by ten. Lower bound is artificially set
+ * at 0.5.
+ * @param db decibel value multiplied by ten, describing gain/attenuation at
+ * peak freq. Max value is 24 dB.
+ * @param c pointer to coefficient storage. Coefficients are s3.28 format.
+ */
+void filter_pk_coefs(unsigned long cutoff, unsigned long Q, long db,
+ struct dsp_filter *f)
+{
+ long cs;
+ const long one = 1 << 28; /* s3.28 */
+ const long A = get_replaygain_int(db*5) << 5; /* 10^(db/40), s2.29 */
+ const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
+ int32_t a0, a1, a2; /* these are all s3.28 format */
+ int32_t b0, b1, b2;
+ const long alphadivA = fp_div(alpha, A, 27);
+ const long alphaA = FRACMUL(alpha, A);
+
+ /* possible numerical ranges are in comments by each coef */
+ b0 = one + alphaA; /* [1 .. 5] */
+ b1 = a1 = -2*(cs >> 3); /* [-2 .. 2] */
+ b2 = one - alphaA; /* [-3 .. 1] */
+ a0 = one + alphadivA; /* [1 .. 5] */
+ a2 = one - alphadivA; /* [-3 .. 1] */
+
+ /* range of this is roughly [0.2 .. 1], but we'll never hit 1 completely */
+ int32_t *c = f->coefs;
+ const long rcp_a0 = fp_div(1, a0, 59); /* s0.31 */
+ *c++ = FRACMUL(b0, rcp_a0); /* [0.25 .. 4] */
+ *c++ = FRACMUL(b1, rcp_a0); /* [-2 .. 2] */
+ *c++ = FRACMUL(b2, rcp_a0); /* [-2.4 .. 1] */
+ *c++ = FRACMUL(-a1, rcp_a0); /* [-2 .. 2] */
+ *c = FRACMUL(-a2, rcp_a0); /* [-0.6 .. 1] */
+
+ f->shift = FILTER_PEAK_SHIFT;
+}
+
+/**
+ * Calculate coefficients for lowshelf filter. Parameters are as for
+ * filter_pk_coefs, but the coefficient format is s5.26 fixed point.
+ */
+void filter_ls_coefs(unsigned long cutoff, unsigned long Q, long db,
+ struct dsp_filter *f)
+{
+ long cs;
+ const long one = 1 << 25; /* s6.25 */
+ const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
+ const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
+ const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
+ const long ap1 = (A >> 4) + one;
+ const long am1 = (A >> 4) - one;
+ const long ap1_cs = FRACMUL(ap1, cs);
+ const long am1_cs = FRACMUL(am1, cs);
+ const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
+ int32_t a0, a1, a2; /* these are all s6.25 format */
+ int32_t b0, b1, b2;
+
+ /* [0.1 .. 40] */
+ b0 = FRACMUL_SHL(A, ap1 - am1_cs + twosqrtalpha, 2);
+ /* [-16 .. 63.4] */
+ b1 = FRACMUL_SHL(A, am1 - ap1_cs, 3);
+ /* [0 .. 31.7] */
+ b2 = FRACMUL_SHL(A, ap1 - am1_cs - twosqrtalpha, 2);
+ /* [0.5 .. 10] */
+ a0 = ap1 + am1_cs + twosqrtalpha;
+ /* [-16 .. 4] */
+ a1 = -2*(am1 + ap1_cs);
+ /* [0 .. 8] */
+ a2 = ap1 + am1_cs - twosqrtalpha;
+
+ /* [0.1 .. 1.99] */
+ int32_t *c = f->coefs;
+ const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
+ *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */
+ *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */
+ *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */
+ *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
+ *c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
+
+ f->shift = FILTER_SHELF_SHIFT;
+}
+
+/**
+ * Calculate coefficients for highshelf filter. Parameters are as for
+ * filter_pk_coefs, but the coefficient format is s5.26 fixed point.
+ */
+void filter_hs_coefs(unsigned long cutoff, unsigned long Q, long db,
+ struct dsp_filter *f)
+{
+ long cs;
+ const long one = 1 << 25; /* s6.25 */
+ const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
+ const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
+ const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
+ const long ap1 = (A >> 4) + one;
+ const long am1 = (A >> 4) - one;
+ const long ap1_cs = FRACMUL(ap1, cs);
+ const long am1_cs = FRACMUL(am1, cs);
+ const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
+ int32_t a0, a1, a2; /* these are all s6.25 format */
+ int32_t b0, b1, b2;
+
+ /* [0.1 .. 40] */
+ b0 = FRACMUL_SHL(A, ap1 + am1_cs + twosqrtalpha, 2);
+ /* [-63.5 .. 16] */
+ b1 = -FRACMUL_SHL(A, am1 + ap1_cs, 3);
+ /* [0 .. 32] */
+ b2 = FRACMUL_SHL(A, ap1 + am1_cs - twosqrtalpha, 2);
+ /* [0.5 .. 10] */
+ a0 = ap1 - am1_cs + twosqrtalpha;
+ /* [-4 .. 16] */
+ a1 = 2*(am1 - ap1_cs);
+ /* [0 .. 8] */
+ a2 = ap1 - am1_cs - twosqrtalpha;
+
+ /* [0.1 .. 1.99] */
+ int32_t *c = f->coefs;
+ const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
+ *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */
+ *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */
+ *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */
+ *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
+ *c = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
+
+ f->shift = FILTER_SHELF_SHIFT;
+}
+
+/**
+ * Copy filter definition without destroying dst's history
+ */
+void filter_copy(struct dsp_filter *dst, const struct dsp_filter *src)
+{
+ memcpy(dst->coefs, src->coefs, sizeof (src->coefs));
+ dst->shift = src->shift;
+}
+
+/**
+ * Clear filter sample history
+ */
+void filter_flush(struct dsp_filter *f)
+{
+ memset(f->history, 0, sizeof (f->history));
+}
+
+/**
+ * We realise the filters as a second order direct form 1 structure. Direct
+ * form 1 was chosen because of better numerical properties for fixed point
+ * implementations.
+ */
+#if (!defined(CPU_COLDFIRE) && !defined(CPU_ARM))
+void filter_process(struct dsp_filter *f, int32_t * const buf[], int count,
+ unsigned int channels)
+{
+ /* Direct form 1 filtering code.
+ y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
+ where y[] is output and x[] is input.
+ */
+ unsigned int shift = f->shift;
+
+ for (unsigned int c = 0; c < channels; c++) {
+ for (int i = 0; i < count; i++) {
+ long long acc = (long long) buf[c][i] * f->coefs[0];
+ acc += (long long) f->history[c][0] * f->coefs[1];
+ acc += (long long) f->history[c][1] * f->coefs[2];
+ acc += (long long) f->history[c][2] * f->coefs[3];
+ acc += (long long) f->history[c][3] * f->coefs[4];
+ f->history[c][1] = f->history[c][0];
+ f->history[c][0] = buf[c][i];
+ f->history[c][3] = f->history[c][2];
+ buf[c][i] = (acc << shift) >> 32;
+ f->history[c][2] = buf[c][i];
+ }
+ }
+}
+#endif /* CPU */
diff --git a/lib/rbcodec/dsp/dsp_filter.h b/lib/rbcodec/dsp/dsp_filter.h
new file mode 100644
index 0000000000..af6e20ce86
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_filter.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Thom Johansen
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef DSP_FILTER_H
+#define DSP_FILTER_H
+
+/** Basic filter implementations which may be used independently **/
+
+/* Used by: EQ, tone controls and crossfeed */
+
+/* These depend on the fixed point formats used by the different filter types
+ and need to be changed when they change.
+ */
+struct dsp_filter
+{
+ int32_t coefs[5]; /* 00h: Order is b0, b1, b2, a1, a2 */
+ int32_t history[2][4]; /* 14h: Order is x-1, x-2, y-1, y-2, per channel */
+ uint8_t shift; /* 34h: Final shift after computation */
+ /* 38h */
+};
+
+void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c);
+#ifdef HAVE_SW_TONE_CONTROLS
+void filter_bishelf_coefs(unsigned long cutoff_low,
+ unsigned long cutoff_high,
+ long A_low, long A_high, long A,
+ struct dsp_filter *f);
+#endif /* HAVE_SW_TONE_CONTROLS */
+void filter_pk_coefs(unsigned long cutoff, unsigned long Q, long db,
+ struct dsp_filter *f);
+void filter_ls_coefs(unsigned long cutoff, unsigned long Q, long db,
+ struct dsp_filter *f);
+void filter_hs_coefs(unsigned long cutoff, unsigned long Q, long db,
+ struct dsp_filter *f);
+void filter_copy(struct dsp_filter *dst, const struct dsp_filter *src);
+void filter_flush(struct dsp_filter *f);
+void filter_process(struct dsp_filter *f, int32_t * const buf[], int count,
+ unsigned int channels);
+
+#endif /* DSP_FILTER_H */
diff --git a/lib/rbcodec/dsp/dsp_misc.c b/lib/rbcodec/dsp/dsp_misc.c
new file mode 100644
index 0000000000..7b4589151c
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_misc.c
@@ -0,0 +1,238 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ * Copyright (C) 2005 Magnus Holmgren
+ * Copyright (C) 2007 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 "dsp.h"
+#include "dsp_sample_io.h"
+#include "replaygain.h"
+#include "sound.h"
+#include "settings.h"
+#include "fixedpoint.h"
+#include <string.h>
+#include "dsp_proc_entry.h"
+
+/** Firmware callback interface **/
+
+/* Hook back from firmware/ part of audio, which can't/shouldn't call apps/
+ * code directly. */
+int dsp_callback(int msg, intptr_t param)
+{
+ switch (msg)
+ {
+#ifdef HAVE_SW_TONE_CONTROLS
+ case DSP_CALLBACK_SET_PRESCALE:
+ tone_set_prescale(param);
+ break;
+ case DSP_CALLBACK_SET_BASS:
+ tone_set_bass(param);
+ break;
+ case DSP_CALLBACK_SET_TREBLE:
+ tone_set_treble(param);
+ break;
+ /* FIXME: This must be done by bottom-level PCM driver so it works with
+ all PCM, not here and not in mixer. I won't fully support it
+ here with all streams. -- jethead71 */
+#ifdef HAVE_SW_VOLUME_CONTROL
+ case DSP_CALLBACK_SET_SW_VOLUME:
+ if (global_settings.volume < SW_VOLUME_MAX ||
+ global_settings.volume > SW_VOLUME_MIN)
+ {
+ int vol_gain = get_replaygain_int(global_settings.volume * 100);
+ pga_set_gain(PGA_VOLUME, vol_gain);
+ }
+ break;
+#endif /* HAVE_SW_VOLUME_CONTROL */
+#endif /* HAVE_SW_TONE_CONTROLS */
+ case DSP_CALLBACK_SET_CHANNEL_CONFIG:
+ channel_mode_set_config(param);
+ break;
+ case DSP_CALLBACK_SET_STEREO_WIDTH:
+ channel_mode_custom_set_width(param);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/** Replaygain settings **/
+static struct dsp_replay_gains current_rpgains;
+
+static void dsp_replaygain_update(const struct dsp_replay_gains *gains)
+{
+ if (gains == NULL)
+ {
+ /* Use defaults */
+ memset(&current_rpgains, 0, sizeof (current_rpgains));
+ gains = &current_rpgains;
+ }
+ else
+ {
+ current_rpgains = *gains; /* Stash settings */
+ }
+
+ int32_t gain = PGA_UNITY;
+
+ if (global_settings.replaygain_type != REPLAYGAIN_OFF ||
+ global_settings.replaygain_noclip)
+ {
+ bool track_mode =
+ get_replaygain_mode(gains->track_gain != 0,
+ gains->album_gain != 0) == REPLAYGAIN_TRACK;
+
+ int32_t peak = (track_mode || gains->album_peak == 0) ?
+ gains->track_peak : gains->album_peak;
+
+ if (global_settings.replaygain_type != REPLAYGAIN_OFF)
+ {
+ gain = (track_mode || gains->album_gain == 0) ?
+ gains->track_gain : gains->album_gain;
+
+ if (global_settings.replaygain_preamp)
+ {
+ int32_t preamp = get_replaygain_int(
+ global_settings.replaygain_preamp * 10);
+
+ gain = fp_mul(gain, preamp, 24);
+ }
+ }
+
+ if (gain == 0)
+ {
+ /* So that noclip can work even with no gain information. */
+ gain = PGA_UNITY;
+ }
+
+ if (global_settings.replaygain_noclip && peak != 0 &&
+ fp_mul(gain, peak, 24) >= PGA_UNITY)
+ {
+ gain = fp_div(PGA_UNITY, peak, 24);
+ }
+ }
+
+ pga_set_gain(PGA_REPLAYGAIN, gain);
+ pga_enable_gain(PGA_REPLAYGAIN, gain != PGA_UNITY);
+}
+
+int get_replaygain_mode(bool have_track_gain, bool have_album_gain)
+{
+ bool track = false;
+
+ switch (global_settings.replaygain_type)
+ {
+ case REPLAYGAIN_TRACK:
+ track = true;
+ break;
+
+ case REPLAYGAIN_SHUFFLE:
+ track = global_settings.playlist_shuffle;
+ break;
+ }
+
+ return (!track && have_album_gain) ?
+ REPLAYGAIN_ALBUM : (have_track_gain ? REPLAYGAIN_TRACK : -1);
+}
+
+void dsp_set_replaygain(void)
+{
+ dsp_replaygain_update(&current_rpgains);
+}
+
+
+/** Pitch Settings **/
+
+#ifdef HAVE_PITCHSCREEN
+static int32_t pitch_ratio = PITCH_SPEED_100;
+
+static void dsp_pitch_update(struct dsp_config *dsp)
+{
+ /* Account for playback speed adjustment when setting dsp->frequency
+ if we're called from the main audio thread. Voice playback thread
+ does not support this feature. */
+ struct sample_io_data *data = (void *)dsp;
+ data->format.frequency =
+ (int64_t)pitch_ratio * data->format.codec_frequency / PITCH_SPEED_100;
+}
+
+int32_t sound_get_pitch(void)
+{
+ return pitch_ratio;
+}
+
+void sound_set_pitch(int32_t percent)
+{
+ pitch_ratio = percent > 0 ? percent : PITCH_SPEED_100;
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ struct sample_io_data *data = (void *)dsp;
+ dsp_configure(dsp, DSP_SWITCH_FREQUENCY, data->format.codec_frequency);
+}
+#endif /* HAVE_PITCHSCREEN */
+
+/* This is a null-processing stage that monitors as an enabled stage but never
+ * becomes active in processing samples. It only hooks messages. */
+
+/* DSP message hook */
+static intptr_t misc_handler_configure(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned setting,
+ intptr_t value)
+{
+ switch (setting)
+ {
+ case DSP_INIT:
+ /* Enable us for the audio DSP at startup */
+ if (value == CODEC_IDX_AUDIO)
+ dsp_proc_enable(dsp, DSP_PROC_MISC_HANDLER, true);
+ break;
+
+ case DSP_PROC_CLOSE:
+ /* This stage should be enabled at all times */
+ DEBUGF("DSP_PROC_MISC_HANDLER - Error: Closing!\n");
+ break;
+
+ case DSP_RESET:
+#ifdef HAVE_PITCHSCREEN
+ dsp_pitch_update(dsp);
+#endif
+ value = (intptr_t)NULL; /* Default gains */
+ case REPLAYGAIN_SET_GAINS:
+ dsp_replaygain_update((void *)value);
+ break;
+
+#ifdef HAVE_PITCHSCREEN
+ case DSP_SET_FREQUENCY:
+ dsp_pitch_update(dsp);
+ break;
+#endif
+ }
+
+ return 1;
+ (void)this;
+}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(
+ MISC_HANDLER,
+ misc_handler_configure);
diff --git a/lib/rbcodec/dsp/dsp_misc.h b/lib/rbcodec/dsp/dsp_misc.h
new file mode 100644
index 0000000000..74587cbb0e
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_misc.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ * Copyright (C) 2005 Magnus Holmgren
+ * Copyright (C) 2007 Thom Johansen
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef DSP_MISC_H
+#define DSP_MISC_H
+
+/* Set the tri-pdf dithered output */
+void dsp_dither_enable(bool enable); /* in dsp_sample_output.c */
+
+/* Structure used with REPLAYGAIN_SET_GAINS message */
+#define REPLAYGAIN_SET_GAINS (DSP_PROC_SETTING+DSP_PROC_MISC_HANDLER)
+struct dsp_replay_gains
+{
+ long track_gain;
+ long album_gain;
+ long track_peak;
+ long album_peak;
+};
+
+int get_replaygain_mode(bool have_track_gain, bool have_album_gain);
+void dsp_set_replaygain(void);
+
+#ifdef HAVE_PITCHSCREEN
+void sound_set_pitch(int32_t ratio);
+int32_t sound_get_pitch(void);
+#endif /* HAVE_PITCHSCREEN */
+
+/* Callback for firmware layers to interface */
+int dsp_callback(int msg, intptr_t param);
+
+#endif /* DSP_MISC_H */
diff --git a/lib/rbcodec/dsp/dsp_proc_database.h b/lib/rbcodec/dsp/dsp_proc_database.h
new file mode 100644
index 0000000000..55f10e684b
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_proc_database.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * -_-~-_-~-_-~-_-~-_-~-_- Main database of effects _-~-_-~-_-~-_-~-_-~-_-~-
+ *
+ * Order is not particularly relevant and has no intended correlation with
+ * IDs.
+ *
+ * Notable exceptions in ordering:
+ * * Sample input: which is first in line and has special responsibilities
+ * (not an effect per se).
+ * * Anything that depends on the native sample rate must go after the
+ * resampling stage.
+ * * Some bizarre dependency I didn't think of but you decided to implement.
+ * * Sample output: Naturally, this takes the final result and converts it
+ * to the target PCM format (not an effect per se).
+ */
+DSP_PROC_DB_START
+ DSP_PROC_DB_ITEM(MISC_HANDLER) /* misc stuff (null stage) */
+ DSP_PROC_DB_ITEM(PGA) /* pre-gain amp */
+#ifdef HAVE_PITCHSCREEN
+ DSP_PROC_DB_ITEM(TIMESTRETCH) /* time-stretching */
+#endif
+ DSP_PROC_DB_ITEM(RESAMPLE) /* resampler providing NATIVE_FREQUENCY */
+ DSP_PROC_DB_ITEM(CROSSFEED) /* stereo crossfeed */
+ DSP_PROC_DB_ITEM(EQUALIZER) /* n-band equalizer */
+#ifdef HAVE_SW_TONE_CONTROLS
+ DSP_PROC_DB_ITEM(TONE_CONTROLS) /* bass and treble */
+#endif
+ DSP_PROC_DB_ITEM(CHANNEL_MODE) /* channel modes */
+ DSP_PROC_DB_ITEM(COMPRESSOR) /* dynamic-range compressor */
+DSP_PROC_DB_STOP
+
+/* This file is included multiple times with different macro definitions so
+ clean up the current ones */
+#undef DSP_PROC_DB_START
+#undef DSP_PROC_DB_ITEM
+#undef DSP_PROC_DB_STOP
diff --git a/lib/rbcodec/dsp/dsp_proc_entry.h b/lib/rbcodec/dsp/dsp_proc_entry.h
new file mode 100644
index 0000000000..8bdfe5e0c9
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_proc_entry.h
@@ -0,0 +1,153 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef DSP_PROC_ENTRY_H
+#define DSP_PROC_ENTRY_H
+
+#if 0 /* Set to '1' to enable local debug messages */
+#include <debug.h>
+#else
+#undef DEBUGF
+#define DEBUGF(...)
+#endif
+
+/* Macros to generate the right stuff */
+#ifdef DSP_PROC_DB_CREATE
+struct dsp_proc_db_entry;
+
+#define DSP_PROC_DB_START
+#define DSP_PROC_DB_ITEM(name) \
+ extern const struct dsp_proc_db_entry name##_proc_db_entry;
+#define DSP_PROC_DB_STOP
+
+/* Create database as externs to be able to build array */
+#include "dsp_proc_database.h"
+
+#define DSP_PROC_DB_START \
+ static struct dsp_proc_db_entry const * const dsp_proc_database[] = {
+
+#define DSP_PROC_DB_ITEM(name) \
+ &name##_proc_db_entry,
+
+#define DSP_PROC_DB_STOP };
+
+/* Create database as array */
+#include "dsp_proc_database.h"
+
+/* Number of effects in database - all available in audio DSP */
+#define DSP_NUM_PROC_STAGES ARRAYLEN(dsp_proc_database)
+
+/* Number of possible effects for voice DSP */
+#ifdef HAVE_SW_TONE_CONTROLS
+#define DSP_VOICE_NUM_PROC_STAGES 2 /* resample, tone */
+#else
+#define DSP_VOICE_NUM_PROC_STAGES 1 /* resample */
+#endif
+
+#else /* !DSP_PROC_DB_CREATE */
+
+#ifdef DEBUG
+#define DSP_PROC_DB_ENTRY(_name, _configure) \
+ const struct dsp_proc_db_entry _name##_proc_db_entry = \
+ { .id = DSP_PROC_##_name, .configure = _configure, \
+ .name = #_name };
+#else /* !DEBUG */
+#define DSP_PROC_DB_ENTRY(_name, _configure) \
+ const struct dsp_proc_db_entry _name##_proc_db_entry = \
+ { .id = DSP_PROC_##_name, .configure = _configure };
+#endif /* DEBUG */
+
+#endif /* DSP_PROC_DB_CREATE */
+
+#define DSP_PROC_DB_START \
+ enum dsp_proc_ids \
+ { \
+ ___DSP_PROC_ID_FIRST = -1,
+
+#define DSP_PROC_DB_ITEM(name) \
+ DSP_PROC_##name,
+
+#define DSP_PROC_DB_STOP };
+
+/* Create database as enums for use as ids */
+#include "dsp_proc_database.h"
+
+struct dsp_proc_entry;
+enum dsp_proc_ids;
+
+/* DSP sample transform function prototype */
+typedef void (*dsp_proc_fn_type)(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf);
+
+/**
+ * dsp_proc_entry
+ * The structure allocated to every stage when enabled.
+ *
+ * default settings:
+ * .data = 0
+ * .ip_mask = BIT_N(dsp_proc_db_entry.id)
+ * .process[0] = dsp_process_null
+ * .process[1] = dsp_format_change_process
+ *
+ * DSP_PROC_INIT handler just has to change what it needs to change. It may
+ * also be modified at any time to implement the stage's demands.
+ */
+struct dsp_proc_entry
+{
+ intptr_t data; /* 00h: any value, at beginning for easy asm use */
+ uint32_t ip_mask; /* In-place id bit (0 or id bit flag if in-place) */
+ dsp_proc_fn_type process[2]; /* Processing normal/format changes */
+};
+
+/* DSP transform configure function prototype */
+typedef intptr_t (*dsp_proc_config_fn_type)(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned int setting,
+ intptr_t value);
+
+/* Enable/disable a processing stage - not to be called during processing
+ * by processing code! */
+void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id,
+ bool enable);
+/* Activate/deactivate processing stage, doesn't affect enabled status
+ * thus will not enable anything -
+ * may be called during processing to activate/deactivate for format
+ * changes */
+void dsp_proc_activate(struct dsp_config *dsp, enum dsp_proc_ids id,
+ bool activate);
+
+/* Is the specified stage active on the DSP? */
+bool dsp_proc_active(struct dsp_config *dsp, enum dsp_proc_ids id);
+
+/* Call this->process[fmt] according to the rules
+ * pass (unsigned)-1 to call function 0 with no restriction */
+bool dsp_proc_call(struct dsp_proc_entry *this, struct dsp_buffer **buf_p,
+ unsigned int fmt);
+
+struct dsp_proc_db_entry
+{
+ enum dsp_proc_ids id; /* id of this stage */
+ dsp_proc_config_fn_type configure; /* dsp_configure hook */
+#ifdef DEBUG
+ const char *name;
+#endif
+};
+
+#endif /* DSP_PROC_ENTRY_H */
diff --git a/lib/rbcodec/dsp/dsp_proc_settings.h b/lib/rbcodec/dsp/dsp_proc_settings.h
new file mode 100644
index 0000000000..769532085e
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_proc_settings.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef DSP_PROC_SETTINGS_H
+#define DSP_PROC_SETTINGS_H
+
+struct dsp_config;
+
+/* Collect all headers together */
+#include "channel_mode.h"
+#include "compressor.h"
+#include "crossfeed.h"
+#include "dsp_misc.h"
+#include "eq.h"
+#include "pga.h"
+#ifdef HAVE_PITCHSCREEN
+#include "tdspeed.h"
+#endif
+#ifdef HAVE_SW_TONE_CONTROLS
+#include "tone_controls.h"
+#endif
+
+#endif /* DSP_PROC_SETTINGS_H */ \ No newline at end of file
diff --git a/lib/rbcodec/dsp/dsp_sample_input.c b/lib/rbcodec/dsp/dsp_sample_input.c
new file mode 100644
index 0000000000..84127e1f96
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_sample_input.c
@@ -0,0 +1,334 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ * 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 "dsp.h"
+#include "dsp_sample_io.h"
+
+#if 1
+#include <debug.h>
+#else
+#undef DEBUGF
+#define DEBUGF(...)
+#endif
+
+/* The internal format is 32-bit samples, non-interleaved, stereo. This
+ * format is similar to the raw output from several codecs, so no copying is
+ * needed for that case.
+ *
+ * Note that for mono, dst[0] equals dst[1], as there is no point in
+ * processing the same data twice nor should it be done when modifying
+ * samples in-place.
+ *
+ * When conversion is required:
+ * Updates source buffer to point past the samples "consumed" also consuming
+ * that portion of the input buffer and the destination is set to the buffer
+ * of samples for later stages to consume.
+ *
+ * Input operates similarly to how an out-of-place processing stage should
+ * behave.
+ */
+
+extern void dsp_sample_output_init(struct sample_io_data *this);
+extern void dsp_sample_output_flush(struct sample_io_data *this);
+
+/* convert count 16-bit mono to 32-bit mono */
+static void sample_input_mono16(struct sample_io_data *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &this->sample_buf;
+
+ *buf_p = dst;
+
+ if (dst->remcount > 0)
+ return; /* data still remains */
+
+ int count = MIN(src->remcount, SAMPLE_BUF_COUNT);
+
+ dst->remcount = count;
+ dst->p32[0] = this->sample_buf_arr[0];
+ dst->p32[1] = this->sample_buf_arr[0];
+ dst->proc_mask = src->proc_mask;
+
+ if (count <= 0)
+ return; /* purged sample_buf */
+
+ const int16_t *s = src->pin[0];
+ int32_t *d = dst->p32[0];
+ const int scale = WORD_SHIFT;
+
+ dsp_advance_buffer_input(src, count, sizeof (int16_t));
+
+ do
+ {
+ *d++ = *s++ << scale;
+ }
+ while (--count > 0);
+}
+
+/* convert count 16-bit interleaved stereo to 32-bit noninterleaved */
+static void sample_input_i_stereo16(struct sample_io_data *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &this->sample_buf;
+
+ *buf_p = dst;
+
+ if (dst->remcount > 0)
+ return; /* data still remains */
+
+ int count = MIN(src->remcount, SAMPLE_BUF_COUNT);
+
+ dst->remcount = count;
+ dst->p32[0] = this->sample_buf_arr[0];
+ dst->p32[1] = this->sample_buf_arr[1];
+ dst->proc_mask = src->proc_mask;
+
+ if (count <= 0)
+ return; /* purged sample_buf */
+
+ const int16_t *s = src->pin[0];
+ int32_t *dl = dst->p32[0];
+ int32_t *dr = dst->p32[1];
+ const int scale = WORD_SHIFT;
+
+ dsp_advance_buffer_input(src, count, 2*sizeof (int16_t));
+
+ do
+ {
+ *dl++ = *s++ << scale;
+ *dr++ = *s++ << scale;
+ }
+ while (--count > 0);
+}
+
+/* convert count 16-bit noninterleaved stereo to 32-bit noninterleaved */
+static void sample_input_ni_stereo16(struct sample_io_data *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &this->sample_buf;
+
+ *buf_p = dst;
+
+ if (dst->remcount > 0)
+ return; /* data still remains */
+
+ int count = MIN(src->remcount, SAMPLE_BUF_COUNT);
+
+ dst->remcount = count;
+ dst->p32[0] = this->sample_buf_arr[0];
+ dst->p32[1] = this->sample_buf_arr[1];
+ dst->proc_mask = src->proc_mask;
+
+ if (count <= 0)
+ return; /* purged sample_buf */
+
+ const int16_t *sl = src->pin[0];
+ const int16_t *sr = src->pin[1];
+ int32_t *dl = dst->p32[0];
+ int32_t *dr = dst->p32[1];
+ const int scale = WORD_SHIFT;
+
+ dsp_advance_buffer_input(src, count, sizeof (int16_t));
+
+ do
+ {
+ *dl++ = *sl++ << scale;
+ *dr++ = *sr++ << scale;
+ }
+ while (--count > 0);
+}
+
+/* convert count 32-bit mono to 32-bit mono */
+static void sample_input_mono32(struct sample_io_data *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *dst = &this->sample_buf;
+
+ if (dst->remcount > 0)
+ {
+ *buf_p = dst;
+ return; /* data still remains */
+ }
+ /* else no buffer switch */
+
+ struct dsp_buffer *src = *buf_p;
+ src->p32[1] = src->p32[0];
+}
+
+
+/* convert count 32-bit interleaved stereo to 32-bit noninterleaved stereo */
+static void sample_input_i_stereo32(struct sample_io_data *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &this->sample_buf;
+
+ *buf_p = dst;
+
+ if (dst->remcount > 0)
+ return; /* data still remains */
+
+ int count = MIN(src->remcount, SAMPLE_BUF_COUNT);
+
+ dst->remcount = count;
+ dst->p32[0] = this->sample_buf_arr[0];
+ dst->p32[1] = this->sample_buf_arr[1];
+ dst->proc_mask = src->proc_mask;
+
+ if (count <= 0)
+ return; /* purged sample_buf */
+
+ const int32_t *s = src->pin[0];
+ int32_t *dl = dst->p32[0];
+ int32_t *dr = dst->p32[1];
+
+ dsp_advance_buffer_input(src, count, 2*sizeof (int32_t));
+
+ do
+ {
+ *dl++ = *s++;
+ *dr++ = *s++;
+ }
+ while (--count > 0);
+}
+
+/* convert 32 bit-noninterleaved stereo to 32-bit noninterleaved stereo */
+static void sample_input_ni_stereo32(struct sample_io_data *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *dst = &this->sample_buf;
+
+ if (dst->remcount > 0)
+ *buf_p = dst; /* data still remains */
+ /* else no buffer switch */
+}
+
+/* set the to-native sample conversion function based on dsp sample
+ * parameters */
+static void dsp_sample_input_format_change(struct sample_io_data *this,
+ struct dsp_buffer **buf_p)
+{
+ static const sample_input_fn_type fns[STEREO_NUM_MODES][2] =
+ {
+ [STEREO_INTERLEAVED] =
+ { sample_input_i_stereo16,
+ sample_input_i_stereo32 },
+ [STEREO_NONINTERLEAVED] =
+ { sample_input_ni_stereo16,
+ sample_input_ni_stereo32 },
+ [STEREO_MONO] =
+ { sample_input_mono16,
+ sample_input_mono32 },
+ };
+
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &this->sample_buf;
+
+ /* Ack configured format change */
+ format_change_ack(&this->format);
+
+ if (dst->remcount > 0)
+ {
+ *buf_p = dst;
+ return; /* data still remains */
+ }
+
+ DSP_PRINT_FORMAT(DSP Input, -1, src->format);
+
+ /* new format - remember it and pass it along */
+ dst->format = src->format;
+ this->input_samples[0] = fns[this->stereo_mode]
+ [this->sample_depth > NATIVE_DEPTH ? 1 : 0];
+
+ this->input_samples[0](this, buf_p);
+
+ if (*buf_p == dst) /* buffer switch? */
+ format_change_ack(&src->format);
+}
+
+static void dsp_sample_input_init(struct sample_io_data *this)
+{
+ this->input_samples[0] = sample_input_ni_stereo32;
+ this->input_samples[1] = dsp_sample_input_format_change;
+}
+
+/* discard the sample buffer */
+static void dsp_sample_input_flush(struct sample_io_data *this)
+{
+ this->sample_buf.remcount = 0;
+}
+
+void dsp_sample_io_configure(struct sample_io_data *this,
+ unsigned int setting,
+ intptr_t value)
+{
+ switch (setting)
+ {
+ case DSP_INIT:
+ dsp_sample_input_init(this);
+ dsp_sample_output_init(this);
+ break;
+
+ case DSP_RESET:
+ /* Reset all sample descriptions to default */
+ format_change_set(&this->format);
+ this->format.num_channels = 2;
+ this->format.frac_bits = WORD_FRACBITS;
+ this->format.output_scale = WORD_FRACBITS + 1 - NATIVE_DEPTH;
+ this->format.frequency = NATIVE_FREQUENCY;
+ this->format.codec_frequency = NATIVE_FREQUENCY;
+ this->sample_depth = NATIVE_DEPTH;
+ this->stereo_mode = STEREO_NONINTERLEAVED;
+ break;
+
+ case DSP_SET_FREQUENCY:
+ value = value > 0 ? value : NATIVE_FREQUENCY;
+ format_change_set(&this->format);
+ this->format.frequency = value;
+ this->format.codec_frequency = value;
+ break;
+
+ case DSP_SET_SAMPLE_DEPTH:
+ format_change_set(&this->format);
+ this->format.frac_bits =
+ value <= NATIVE_DEPTH ? WORD_FRACBITS : value;
+ this->format.output_scale =
+ this->format.frac_bits + 1 - NATIVE_DEPTH;
+ this->sample_depth = value;
+ break;
+
+ case DSP_SET_STEREO_MODE:
+ format_change_set(&this->format);
+ this->format.num_channels = value == STEREO_MONO ? 1 : 2;
+ this->stereo_mode = value;
+ break;
+
+ case DSP_FLUSH:
+ dsp_sample_input_flush(this);
+ dsp_sample_output_flush(this);
+ break;
+ }
+}
diff --git a/lib/rbcodec/dsp/dsp_sample_io.h b/lib/rbcodec/dsp/dsp_sample_io.h
new file mode 100644
index 0000000000..443038919d
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_sample_io.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef DSP_SAMPLE_IO_H
+#define DSP_SAMPLE_IO_H
+
+/* 16-bit samples are scaled based on these constants. The shift should be
+ * no more than 15.
+ */
+#define WORD_SHIFT 12
+#define WORD_FRACBITS 27
+#define NATIVE_DEPTH 16
+
+#define SAMPLE_BUF_COUNT 128 /* Per channel, per DSP */
+
+struct sample_io_data;
+
+/* DSP initial buffer input function call prototype */
+typedef void (*sample_input_fn_type)(struct sample_io_data *this,
+ struct dsp_buffer **buf_p);
+
+/* DSP final buffer output function call prototype */
+typedef void (*sample_output_fn_type)(struct sample_io_data *this,
+ struct dsp_buffer *src,
+ struct dsp_buffer *dst);
+
+/* This becomes part of the DSP aggregate */
+struct sample_io_data
+{
+ int outcount; /* 00h: Output count */
+ struct sample_format format; /* General format info */
+ int sample_depth; /* Codec-specified sample depth */
+ int stereo_mode; /* Codec-specified input format */
+ sample_input_fn_type input_samples[2]; /* input functions */
+ struct dsp_buffer sample_buf; /* Buffer descriptor for converted samples */
+ int32_t sample_buf_arr[2][SAMPLE_BUF_COUNT]; /* Internal format */
+ sample_output_fn_type output_samples[2]; /* Final output functions */
+};
+
+/* Sample IO watches the format setting from the codec */
+void dsp_sample_io_configure(struct sample_io_data *this,
+ unsigned int setting,
+ intptr_t value);
+
+#endif /* DSP_SAMPLE_IO_H */
diff --git a/lib/rbcodec/dsp/dsp_sample_output.c b/lib/rbcodec/dsp/dsp_sample_output.c
new file mode 100644
index 0000000000..47fde0440c
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_sample_output.c
@@ -0,0 +1,214 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ * 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 "dsp.h"
+#include "dsp_sample_io.h"
+#include "dsp-util.h"
+#include <string.h>
+
+#if 0
+#include <debug.h>
+#else
+#undef DEBUGF
+#define DEBUGF(...)
+#endif
+
+/* May be implemented in here or externally.*/
+void sample_output_mono(struct sample_io_data *this,
+ struct dsp_buffer *src, struct dsp_buffer *dst);
+void sample_output_stereo(struct sample_io_data *this,
+ struct dsp_buffer *src, struct dsp_buffer *dst);
+void sample_output_dithered(struct sample_io_data *this,
+ struct dsp_buffer *src, struct dsp_buffer *dst);
+
+/** Sample output **/
+
+#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
+/* write mono internal format to output format */
+void sample_output_mono(struct sample_io_data *this,
+ struct dsp_buffer *src, struct dsp_buffer *dst)
+{
+ int count = this->outcount;
+ const int32_t *s0 = src->p32[0];
+ int16_t *d = dst->p16out;
+ int scale = src->format.output_scale;
+ int32_t dc_bias = 1L << (scale - 1);
+
+ do
+ {
+ int32_t lr = clip_sample_16((*s0++ + dc_bias) >> scale);
+ *d++ = lr;
+ *d++ = lr;
+ }
+ while (--count > 0);
+}
+
+/* write stereo internal format to output format */
+void sample_output_stereo(struct sample_io_data *this,
+ struct dsp_buffer *src, struct dsp_buffer *dst)
+{
+ int count = this->outcount;
+ const int32_t *s0 = src->p32[0];
+ const int32_t *s1 = src->p32[1];
+ int16_t *d = dst->p16out;
+ int scale = src->format.output_scale;
+ int32_t dc_bias = 1L << (scale - 1);
+
+ do
+ {
+ *d++ = clip_sample_16((*s0++ + dc_bias) >> scale);
+ *d++ = clip_sample_16((*s1++ + dc_bias) >> scale);
+ }
+ while (--count > 0);
+}
+#endif /* CPU */
+
+/**
+ * The "dither" code to convert the 24-bit samples produced by libmad was
+ * taken from the coolplayer project - coolplayer.sourceforge.net
+ *
+ * This function handles mono and stereo outputs.
+ */
+static struct dither_data
+{
+ struct dither_state
+ {
+ long error[3]; /* 00h: error term history */
+ long random; /* 0ch: last random value */
+ } state[2]; /* 0=left, 1=right */
+ bool enabled; /* 20h: dithered output enabled */
+ /* 24h */
+} dither_data IBSS_ATTR;
+
+void sample_output_dithered(struct sample_io_data *this,
+ struct dsp_buffer *src, struct dsp_buffer *dst)
+{
+ int count = this->outcount;
+ int channels = src->format.num_channels;
+ int scale = src->format.output_scale;
+ int32_t dc_bias = 1L << (scale - 1); /* 1/2 bit of significance */
+ int32_t mask = (1L << scale) - 1; /* Mask of bits quantized away */
+
+ for (int ch = 0; ch < channels; ch++)
+ {
+ struct dither_state *dither = &dither_data.state[ch];
+
+ const int32_t *s = src->p32[ch];
+ int16_t *d = &dst->p16out[ch];
+
+ for (int i = 0; i < count; i++, s++, d += 2)
+ {
+ /* Noise shape and bias (for correct rounding later) */
+ int32_t sample = *s;
+
+ sample += dither->error[0] - dither->error[1] + dither->error[2];
+ dither->error[2] = dither->error[1];
+ dither->error[1] = dither->error[0] / 2;
+
+ int32_t output = sample + dc_bias;
+
+ /* Dither, highpass triangle PDF */
+ int32_t random = dither->random*0x0019660dL + 0x3c6ef35fL;
+ output += (random & mask) - (dither->random & mask);
+ dither->random = random;
+
+ /* Quantize sample to output range */
+ output >>= scale;
+
+ /* Error feedback of quantization */
+ dither->error[0] = sample - (output << scale);
+
+ /* Clip and store */
+ *d = clip_sample_16(output);
+ }
+ }
+
+ if (channels > 1)
+ return;
+
+ /* Have to duplicate left samples into the right channel since
+ output is interleaved stereo */
+ int16_t *d = dst->p16out;
+
+ do
+ {
+ int16_t s = *d++;
+ *d++ = s;
+ }
+ while (--count > 0);
+}
+
+/* Initialize the output function for settings and format */
+static void dsp_sample_output_format_change(struct sample_io_data *this,
+ struct dsp_buffer *src,
+ struct dsp_buffer *dst)
+{
+ static const sample_output_fn_type fns[2][2] =
+ {
+ { sample_output_mono, /* DC-biased quantizing */
+ sample_output_stereo },
+ { sample_output_dithered, /* Tri-PDF dithering */
+ sample_output_dithered },
+ };
+
+ struct sample_format *format = &src->format;
+ bool dither = dsp_get_id((void *)this) == CODEC_IDX_AUDIO &&
+ dither_data.enabled;
+ int channels = format->num_channels;
+
+ DSP_PRINT_FORMAT(DSP Output, -1, *format);
+
+ this->output_samples[0] = fns[dither ? 1 : 0][channels - 1];
+ format_change_ack(format); /* always ack, we're last */
+
+ /* The real function mustn't be called with no data */
+ if (this->outcount > 0)
+ this->output_samples[0](this, src, dst);
+}
+
+void dsp_sample_output_init(struct sample_io_data *this)
+{
+ this->output_samples[0] = sample_output_stereo;
+ this->output_samples[1] = dsp_sample_output_format_change;
+}
+
+/* Flush the dither history */
+void dsp_sample_output_flush(struct sample_io_data *this)
+{
+ if (dsp_get_id((void *)this) == CODEC_IDX_AUDIO)
+ memset(dither_data.state, 0, sizeof (dither_data.state));
+}
+
+/** Output settings **/
+
+/* Set the tri-pdf dithered output */
+void dsp_dither_enable(bool enable)
+{
+ if (enable == dither_data.enabled)
+ return;
+
+ struct sample_io_data *data = (void *)dsp_get_config(CODEC_IDX_AUDIO);
+ dsp_sample_output_flush(data);
+ dither_data.enabled = enable;
+ data->output_samples[0] = dsp_sample_output_format_change;
+}
diff --git a/lib/rbcodec/dsp/eq.c b/lib/rbcodec/dsp/eq.c
index 122a46a4c5..4e7df9bf5a 100644
--- a/lib/rbcodec/dsp/eq.c
+++ b/lib/rbcodec/dsp/eq.c
@@ -7,7 +7,8 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Copyright (C) 2006-2007 Thom Johansen
+ * Copyright (C) 2006-2007 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
@@ -18,251 +19,156 @@
* KIND, either express or implied.
*
****************************************************************************/
-
-#include <inttypes.h>
#include "config.h"
+#include "system.h"
#include "fixedpoint.h"
#include "fracmul.h"
-#include "eq.h"
+#include "dsp.h"
+#include "dsp_filter.h"
#include "replaygain.h"
+#include <string.h>
+#include "dsp_proc_entry.h"
-/**
- * Calculate first order shelving filter. Filter is not directly usable by the
- * eq_filter() function.
- * @param cutoff shelf midpoint frequency. See eq_pk_coefs for format.
- * @param A decibel value multiplied by ten, describing gain/attenuation of
- * shelf. Max value is 24 dB.
- * @param low true for low-shelf filter, false for high-shelf filter.
- * @param c pointer to coefficient storage. Coefficients are s4.27 format.
+/**
+ * Current setup is one lowshelf filters three peaking filters and one
+ * highshelf filter. Varying the number of shelving filters make no sense,
+ * but adding peaking filters is possible. Check EQ_NUM_BANDS to have
+ * 2 shelving filters and EQ_NUM_BANDS-2 peaking filters.
*/
-void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c)
+
+#if EQ_NUM_BANDS < 3
+/* No good. Expect at least 1 peaking and low/high shelving filters */
+#error Band count must be greater than or equal to 3
+#endif
+
+static struct eq_state
{
- long sin, cos;
- int32_t b0, b1, a0, a1; /* s3.28 */
- const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */
+ uint32_t enabled; /* Mask of enabled bands */
+ uint8_t bands[EQ_NUM_BANDS+1]; /* Indexes of enabled bands */
+ struct dsp_filter filters[EQ_NUM_BANDS]; /* Data for each filter */
+} eq_data IBSS_ATTR;
- sin = fp_sincos(cutoff/2, &cos);
- if (low) {
- const int32_t sin_div_g = fp_div(sin, g, 25);
- const int32_t sin_g = FRACMUL(sin, g);
- cos >>= 3;
- b0 = sin_g + cos; /* 0.25 .. 4.10 */
- b1 = sin_g - cos; /* -1 .. 3.98 */
- a0 = sin_div_g + cos; /* 0.25 .. 4.10 */
- a1 = sin_div_g - cos; /* -1 .. 3.98 */
- } else {
- const int32_t cos_div_g = fp_div(cos, g, 25);
- const int32_t cos_g = FRACMUL(cos, g);
- sin >>= 3;
- b0 = sin + cos_g; /* 0.25 .. 4.10 */
- b1 = sin - cos_g; /* -3.98 .. 1 */
- a0 = sin + cos_div_g; /* 0.25 .. 4.10 */
- a1 = sin - cos_div_g; /* -3.98 .. 1 */
- }
+/* Clear histories of all enabled bands */
+static void eq_flush(void)
+{
+ if (eq_data.enabled == 0)
+ return; /* Not initialized yet/no bands on */
- const int32_t rcp_a0 = fp_div(1, a0, 57); /* 0.24 .. 3.98, s2.29 */
- *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */
- *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */
- *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */
+ for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++)
+ filter_flush(&eq_data.filters[*b]);
}
-#ifdef HAVE_SW_TONE_CONTROLS
-/**
- * Calculate second order section filter consisting of one low-shelf and one
- * high-shelf section.
- * @param cutoff_low low-shelf midpoint frequency. See eq_pk_coefs for format.
- * @param cutoff_high high-shelf midpoint frequency.
- * @param A_low decibel value multiplied by ten, describing gain/attenuation of
- * low-shelf part. Max value is 24 dB.
- * @param A_high decibel value multiplied by ten, describing gain/attenuation of
- * high-shelf part. Max value is 24 dB.
- * @param A decibel value multiplied by ten, describing additional overall gain.
- * @param c pointer to coefficient storage. Coefficients are s4.27 format.
- */
-void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
- long A_low, long A_high, long A, int32_t *c)
+/** DSP interface **/
+
+/* Set the precut gain value */
+void dsp_set_eq_precut(int precut)
{
- const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */
- int32_t c_ls[3], c_hs[3];
+ pga_set_gain(PGA_EQ_PRECUT, get_replaygain_int(precut * -10));
+}
- filter_shelf_coefs(cutoff_low, A_low, true, c_ls);
- filter_shelf_coefs(cutoff_high, A_high, false, c_hs);
- c_ls[0] = FRACMUL(g, c_ls[0]);
- c_ls[1] = FRACMUL(g, c_ls[1]);
+/* Update the filter configuration for the band */
+void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting)
+{
+ static void (* const coef_gen[EQ_NUM_BANDS])(unsigned long cutoff,
+ unsigned long Q, long db,
+ struct dsp_filter *f) =
+ {
+ [0] = filter_ls_coefs,
+ [1 ... EQ_NUM_BANDS-2] = filter_pk_coefs,
+ [EQ_NUM_BANDS-1] = filter_hs_coefs,
+ };
+
+ if (band < 0 || band >= EQ_NUM_BANDS)
+ return;
+
+ /* NOTE: The coef functions assume the EMAC unit is in fractional mode,
+ which it should be, since we're executed from the main thread. */
+
+ uint32_t mask = eq_data.enabled;
+ struct dsp_filter *filter = &eq_data.filters[band];
+
+ /* Assume a band is disabled if the gain is zero */
+ mask &= ~BIT_N(band);
+
+ if (setting->gain != 0)
+ {
+ mask |= BIT_N(band);
+
+ /* Convert user settings to format required by coef generator
+ functions */
+ coef_gen[band](0xffffffff / NATIVE_FREQUENCY * setting->cutoff,
+ setting->q ?: 1, setting->gain, filter);
+ }
- /* now we cascade the two first order filters to one second order filter
- * which can be used by eq_filter(). these resulting coefficients have a
- * really wide numerical range, so we use a fixed point format which will
- * work for the selected cutoff frequencies (in dsp.c) only.
- */
- const int32_t b0 = c_ls[0], b1 = c_ls[1], b2 = c_hs[0], b3 = c_hs[1];
- const int32_t a0 = c_ls[2], a1 = c_hs[2];
- *c++ = FRACMUL_SHL(b0, b2, 4);
- *c++ = FRACMUL_SHL(b0, b3, 4) + FRACMUL_SHL(b1, b2, 4);
- *c++ = FRACMUL_SHL(b1, b3, 4);
- *c++ = a0 + a1;
- *c++ = -FRACMUL_SHL(a0, a1, 4);
-}
-#endif
+ if (mask == eq_data.enabled)
+ return; /* No change in band-enable state */
-/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
- * Slightly faster calculation can be done by deriving forms which use tan()
- * instead of cos() and sin(), but the latter are far easier to use when doing
- * fixed point math, and performance is not a big point in the calculation part.
- * All the 'a' filter coefficients are negated so we can use only additions
- * in the filtering equation.
- */
+ if (mask & BIT_N(band))
+ filter_flush(filter); /* Coming online */
-/**
- * Calculate second order section peaking filter coefficients.
- * @param cutoff a value from 0 to 0x80000000, where 0 represents 0 Hz and
- * 0x80000000 represents the Nyquist frequency (samplerate/2).
- * @param Q Q factor value multiplied by ten. Lower bound is artificially set
- * at 0.5.
- * @param db decibel value multiplied by ten, describing gain/attenuation at
- * peak freq. Max value is 24 dB.
- * @param c pointer to coefficient storage. Coefficients are s3.28 format.
- */
-void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
-{
- long cs;
- const long one = 1 << 28; /* s3.28 */
- const long A = get_replaygain_int(db*5) << 5; /* 10^(db/40), s2.29 */
- const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
- int32_t a0, a1, a2; /* these are all s3.28 format */
- int32_t b0, b1, b2;
- const long alphadivA = fp_div(alpha, A, 27);
- const long alphaA = FRACMUL(alpha, A);
+ eq_data.enabled = mask;
- /* possible numerical ranges are in comments by each coef */
- b0 = one + alphaA; /* [1 .. 5] */
- b1 = a1 = -2*(cs >> 3); /* [-2 .. 2] */
- b2 = one - alphaA; /* [-3 .. 1] */
- a0 = one + alphadivA; /* [1 .. 5] */
- a2 = one - alphadivA; /* [-3 .. 1] */
+ /* Only be active if there are bands to process - if EQ is off, then
+ this call has no effect */
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, mask != 0);
+
+ /* Prepare list of enabled bands for efficient iteration */
+ for (band = 0; mask != 0; mask &= mask - 1, band++)
+ eq_data.bands[band] = (uint8_t)find_first_set_bit(mask);
- /* range of this is roughly [0.2 .. 1], but we'll never hit 1 completely */
- const long rcp_a0 = fp_div(1, a0, 59); /* s0.31 */
- *c++ = FRACMUL(b0, rcp_a0); /* [0.25 .. 4] */
- *c++ = FRACMUL(b1, rcp_a0); /* [-2 .. 2] */
- *c++ = FRACMUL(b2, rcp_a0); /* [-2.4 .. 1] */
- *c++ = FRACMUL(-a1, rcp_a0); /* [-2 .. 2] */
- *c++ = FRACMUL(-a2, rcp_a0); /* [-0.6 .. 1] */
+ eq_data.bands[band] = EQ_NUM_BANDS;
}
-/**
- * Calculate coefficients for lowshelf filter. Parameters are as for
- * eq_pk_coefs, but the coefficient format is s5.26 fixed point.
- */
-void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
+/* Enable or disable the equalizer */
+void dsp_eq_enable(bool enable)
{
- long cs;
- const long one = 1 << 25; /* s6.25 */
- const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
- const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
- const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
- const long ap1 = (A >> 4) + one;
- const long am1 = (A >> 4) - one;
- const long ap1_cs = FRACMUL(ap1, cs);
- const long am1_cs = FRACMUL(am1, cs);
- const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
- int32_t a0, a1, a2; /* these are all s6.25 format */
- int32_t b0, b1, b2;
-
- /* [0.1 .. 40] */
- b0 = FRACMUL_SHL(A, ap1 - am1_cs + twosqrtalpha, 2);
- /* [-16 .. 63.4] */
- b1 = FRACMUL_SHL(A, am1 - ap1_cs, 3);
- /* [0 .. 31.7] */
- b2 = FRACMUL_SHL(A, ap1 - am1_cs - twosqrtalpha, 2);
- /* [0.5 .. 10] */
- a0 = ap1 + am1_cs + twosqrtalpha;
- /* [-16 .. 4] */
- a1 = -2*(am1 + ap1_cs);
- /* [0 .. 8] */
- a2 = ap1 + am1_cs - twosqrtalpha;
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ dsp_proc_enable(dsp, DSP_PROC_EQUALIZER, enable);
- /* [0.1 .. 1.99] */
- const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
- *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */
- *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */
- *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */
- *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
- *c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
+ if (enable && eq_data.enabled != 0)
+ dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, true);
}
-/**
- * Calculate coefficients for highshelf filter. Parameters are as for
- * eq_pk_coefs, but the coefficient format is s5.26 fixed point.
- */
-void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
+/* Apply EQ filters to those bands that have got it switched on. */
+static void eq_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
{
- long cs;
- const long one = 1 << 25; /* s6.25 */
- const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
- const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
- const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
- const long ap1 = (A >> 4) + one;
- const long am1 = (A >> 4) - one;
- const long ap1_cs = FRACMUL(ap1, cs);
- const long am1_cs = FRACMUL(am1, cs);
- const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
- int32_t a0, a1, a2; /* these are all s6.25 format */
- int32_t b0, b1, b2;
+ struct dsp_buffer *buf = *buf_p;
+ int count = buf->remcount;
+ unsigned int channels = buf->format.num_channels;
- /* [0.1 .. 40] */
- b0 = FRACMUL_SHL(A, ap1 + am1_cs + twosqrtalpha, 2);
- /* [-63.5 .. 16] */
- b1 = -FRACMUL_SHL(A, am1 + ap1_cs, 3);
- /* [0 .. 32] */
- b2 = FRACMUL_SHL(A, ap1 + am1_cs - twosqrtalpha, 2);
- /* [0.5 .. 10] */
- a0 = ap1 - am1_cs + twosqrtalpha;
- /* [-4 .. 16] */
- a1 = 2*(am1 - ap1_cs);
- /* [0 .. 8] */
- a2 = ap1 - am1_cs - twosqrtalpha;
+ for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++)
+ filter_process(&eq_data.filters[*b], buf->p32, count, channels);
- /* [0.1 .. 1.99] */
- const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
- *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */
- *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */
- *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */
- *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
- *c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
+ (void)this;
}
-/* We realise the filters as a second order direct form 1 structure. Direct
- * form 1 was chosen because of better numerical properties for fixed point
- * implementations.
- */
-
-#if (!defined(CPU_COLDFIRE) && !defined(CPU_ARM))
-void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
- unsigned channels, unsigned shift)
+/* DSP message hook */
+static intptr_t eq_configure(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned int setting,
+ intptr_t value)
{
- unsigned c, i;
- long long acc;
-
- /* Direct form 1 filtering code.
- y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
- where y[] is output and x[] is input.
- */
-
- for (c = 0; c < channels; c++) {
- for (i = 0; i < num; i++) {
- acc = (long long) x[c][i] * f->coefs[0];
- acc += (long long) f->history[c][0] * f->coefs[1];
- acc += (long long) f->history[c][1] * f->coefs[2];
- acc += (long long) f->history[c][2] * f->coefs[3];
- acc += (long long) f->history[c][3] * f->coefs[4];
- f->history[c][1] = f->history[c][0];
- f->history[c][0] = x[c][i];
- f->history[c][3] = f->history[c][2];
- x[c][i] = (acc << shift) >> 32;
- f->history[c][2] = x[c][i];
- }
+ switch (setting)
+ {
+ case DSP_PROC_INIT:
+ if (value != 0)
+ break;
+ this->process[0] = eq_process;
+ case DSP_PROC_CLOSE:
+ pga_enable_gain(PGA_EQ_PRECUT, setting == DSP_PROC_INIT);
+ break;
+
+ case DSP_FLUSH:
+ eq_flush();
+ break;
}
+
+ return 1;
+ (void)dsp;
}
-#endif
+/* Database entry */
+DSP_PROC_DB_ENTRY(EQUALIZER,
+ eq_configure);
diff --git a/lib/rbcodec/dsp/eq.h b/lib/rbcodec/dsp/eq.h
index a44e9153ac..53097beb12 100644
--- a/lib/rbcodec/dsp/eq.h
+++ b/lib/rbcodec/dsp/eq.h
@@ -18,33 +18,25 @@
* KIND, either express or implied.
*
****************************************************************************/
-
#ifndef _EQ_H
#define _EQ_H
-#include <inttypes.h>
-#include <stdbool.h>
-
-/* These depend on the fixed point formats used by the different filter types
- and need to be changed when they change.
- */
-#define FILTER_BISHELF_SHIFT 5
-#define EQ_PEAK_SHIFT 4
-#define EQ_SHELF_SHIFT 6
+/* => support from 3 to 32 bands, inclusive
+ * Menus and screens must be updated to support changing this from 5
+ * without modifying other stuff (remove comment when this is no longer
+ * true :-) */
+#define EQ_NUM_BANDS 5
-struct eqfilter {
- int32_t coefs[5]; /* Order is b0, b1, b2, a1, a2 */
- int32_t history[2][4];
+struct eq_band_setting
+{
+ int cutoff; /* Hz */
+ int q;
+ int gain; /* +/- dB */
};
-void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c);
-void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
- long A_low, long A_high, long A, int32_t *c);
-void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
-void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
-void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c);
-void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
- unsigned channels, unsigned shift);
-
-#endif
+/** DSP interface **/
+void dsp_set_eq_precut(int precut);
+void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting);
+void dsp_eq_enable(bool enable);
+#endif /* _EQ_H */
diff --git a/lib/rbcodec/dsp/eq_arm.S b/lib/rbcodec/dsp/eq_arm.S
deleted file mode 100644
index b0e1771e89..0000000000
--- a/lib/rbcodec/dsp/eq_arm.S
+++ /dev/null
@@ -1,89 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006-2007 Thom Johansen
- *
- * 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"
-
-/* uncomment this to make filtering calculate lower bits after shifting.
- * without this, "shift" of the lower bits will be lost here.
- */
-/* #define HIGH_PRECISION */
-
-/*
- * void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
- * unsigned channels, unsigned shift)
- */
-#if CONFIG_CPU == PP5002
- .section .icode,"ax",%progbits
-#else
- .text
-#endif
- .global eq_filter
-eq_filter:
- ldr r12, [sp] @ get shift parameter
- stmdb sp!, { r0-r11, lr } @ save all params and clobbered regs
- ldmia r1!, { r4-r8 } @ load coefs
- mov r10, r1 @ loop prelude expects filter struct addr in r10
-
-.filterloop:
- ldr r9, [sp] @ get pointer to this channels data
- add r0, r9, #4
- str r0, [sp] @ save back pointer to next channels data
- ldr r9, [r9] @ r9 = x[]
- ldr r14, [sp, #8] @ r14 = numsamples
- ldmia r10, { r0-r3 } @ load history, r10 should be filter struct addr
- str r10, [sp, #4] @ save it for loop end
-
- /* r0-r3 = history, r4-r8 = coefs, r9 = x[], r10..r11 = accumulator,
- * r12 = shift amount, r14 = number of samples.
- */
-.loop:
- /* Direct form 1 filtering code.
- * y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
- * where y[] is output and x[] is input. This is performed out of order to
- * reuse registers, we're pretty short on regs.
- */
- smull r10, r11, r6, r1 @ acc = b2*x[i - 2]
- mov r1, r0 @ fix input history
- smlal r10, r11, r5, r0 @ acc += b1*x[i - 1]
- ldr r0, [r9] @ load input and fix history in same operation
- smlal r10, r11, r7, r2 @ acc += a1*y[i - 1]
- smlal r10, r11, r8, r3 @ acc += a2*y[i - 2]
- smlal r10, r11, r4, r0 @ acc += b0*x[i] /* avoid stall on arm9*/
- mov r3, r2 @ fix output history
- mov r2, r11, asl r12 @ get upper part of result and shift left
-#ifdef HIGH_PRECISION
- rsb r11, r12, #32 @ get shift amount for lower part
- orr r2, r2, r10, lsr r11 @ then mix in correctly shifted lower part
-#endif
- str r2, [r9], #4 @ save result
- subs r14, r14, #1 @ are we done with this channel?
- bne .loop
-
- ldr r10, [sp, #4] @ load filter struct pointer
- stmia r10!, { r0-r3 } @ save back history
- ldr r11, [sp, #12] @ load number of channels
- subs r11, r11, #1 @ all channels processed?
- strne r11, [sp, #12]
- bne .filterloop
-
- add sp, sp, #16 @ compensate for temp storage
- ldmpc regs=r4-r11
-
diff --git a/lib/rbcodec/dsp/eq_cf.S b/lib/rbcodec/dsp/eq_cf.S
deleted file mode 100644
index 30a28b9d99..0000000000
--- a/lib/rbcodec/dsp/eq_cf.S
+++ /dev/null
@@ -1,91 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006-2007 Thom Johansen
- *
- * 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.
- *
- ****************************************************************************/
-
-/* uncomment this to make filtering calculate lower bits after shifting.
- * without this, "shift" - 1 of the lower bits will be lost here.
- */
-/* #define HIGH_PRECISION */
-
-/*
- * void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
- * unsigned channels, unsigned shift)
- */
- .text
- .global eq_filter
-eq_filter:
- lea.l (-11*4, %sp), %sp
- movem.l %d2-%d7/%a2-%a6, (%sp) | save clobbered regs
- move.l (11*4+8, %sp), %a5 | fetch filter structure address
- move.l (11*4+20, %sp), %d7 | load shift count
- subq.l #1, %d7 | EMAC gives us one free shift
-#ifdef HIGH_PRECISION
- moveq.l #8, %d6
- sub.l %d7, %d6 | shift for lower part of accumulator
-#endif
- movem.l (%a5), %a0-%a4 | load coefs
- lea.l (5*4, %a5), %a5 | point to filter history
-
-.filterloop:
- move.l (11*4+4, %sp), %a6 | load input channel pointer
- addq.l #4, (11*4+4, %sp) | point x to next channel
- move.l (%a6), %a6
- move.l (11*4+12, %sp), %d5 | number of samples
- movem.l (%a5), %d0-%d3 | load filter history
-
- /* d0-d3 = history, d4 = temp, d5 = sample count, d6 = lower shift amount,
- * d7 = upper shift amount, a0-a4 = coefs, a5 = history pointer, a6 = x[]
- */
-.loop:
- /* Direct form 1 filtering code. We assume DSP has put EMAC in frac mode.
- * y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
- * where y[] is output and x[] is input. This is performed out of order
- * to do parallel load of input value.
- */
- mac.l %a2, %d1, %acc0 | acc = b2*x[i - 2]
- move.l %d0, %d1 | fix input history
- mac.l %a1, %d0, (%a6), %d0, %acc0 | acc += b1*x[i - 1], x[i] -> d0
- mac.l %a0, %d0, %acc0 | acc += b0*x[i]
- mac.l %a3, %d2, %acc0 | acc += a1*y[i - 1]
- mac.l %a4, %d3, %acc0 | acc += a2*y[i - 2]
- move.l %d2, %d3 | fix output history
-#ifdef HIGH_PRECISION
- move.l %accext01, %d2 | fetch lower part of accumulator
- move.b %d2, %d4 | clear upper three bytes
- lsr.l %d6, %d4 | shift lower bits
-#endif
- movclr.l %acc0, %d2 | fetch upper part of result
- asl.l %d7, %d2 | restore fixed point format
-#ifdef HIGH_PRECISION
- or.l %d2, %d4 | combine lower and upper parts
-#endif
- move.l %d2, (%a6)+ | save result
- subq.l #1, %d5 | are we done with this channel?
- jne .loop
-
- movem.l %d0-%d3, (%a5) | save history back to struct
- lea.l (4*4, %a5), %a5 | point to next channel's history
- subq.l #1, (11*4+16, %sp) | have we processed both channels?
- jne .filterloop
-
- movem.l (%sp), %d2-%d7/%a2-%a6
- lea.l (11*4, %sp), %sp
- rts
-
diff --git a/lib/rbcodec/dsp/lin_resample.c b/lib/rbcodec/dsp/lin_resample.c
new file mode 100644
index 0000000000..c8be3cb1ad
--- /dev/null
+++ b/lib/rbcodec/dsp/lin_resample.c
@@ -0,0 +1,281 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ * 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 "dsp.h"
+#include "fracmul.h"
+#include "fixedpoint.h"
+#include "dsp_sample_io.h"
+#include <string.h>
+#include "dsp_proc_entry.h"
+
+/**
+ * Linear interpolation resampling that introduces a one sample delay because
+ * of our inability to look into the future at the end of a frame.
+ */
+
+#if 0 /* Set to '1' to enable debug messages */
+#include <debug.h>
+#else
+#undef DEBUGF
+#define DEBUGF(...)
+#endif
+
+#define RESAMPLE_BUF_COUNT 192 /* Per channel, per DSP */
+
+/* Data for each resampler on each DSP */
+static struct resample_data
+{
+ uint32_t delta; /* 00h: Phase delta for each step */
+ uint32_t phase; /* 04h: Current phase [pos16|frac16] */
+ int32_t last_sample[2]; /* 08h: Last samples for interpolation (L+R) */
+ int32_t frequency; /* 10h: Virtual samplerate */
+ /* 14h */
+ struct dsp_config *dsp; /* The DSP for this resampler */
+ struct dsp_buffer resample_buf; /* Buffer descriptor for resampled data */
+ int32_t resample_buf_arr[2][RESAMPLE_BUF_COUNT]; /* Actual output data */
+} resample_data[DSP_COUNT] IBSS_ATTR;
+
+/* Actual worker function. Implemented here or in target assembly code. */
+int lin_resample_resample(struct resample_data *data, struct dsp_buffer *src,
+ struct dsp_buffer *dst);
+
+static void lin_resample_flush_data(struct resample_data *data)
+{
+ data->phase = 0;
+ data->last_sample[0] = 0;
+ data->last_sample[1] = 0;
+}
+
+static void lin_resample_flush(struct dsp_proc_entry *this)
+{
+ struct resample_data *data = (void *)this->data;
+ data->resample_buf.remcount = 0;
+ lin_resample_flush_data(data);
+}
+
+static bool lin_resample_new_delta(struct resample_data *data,
+ struct dsp_buffer *buf)
+{
+ int32_t frequency = buf->format.frequency; /* virtual samplerate */
+
+ data->frequency = frequency;
+ data->delta = fp_div(frequency, NATIVE_FREQUENCY, 16);
+
+ if (frequency == NATIVE_FREQUENCY)
+ {
+ /* NOTE: If fully glitch-free transistions from no resampling to
+ resampling are desired, last_sample history should be maintained
+ even when not resampling. */
+ lin_resample_flush_data(data);
+ return false;
+ }
+
+ return true;
+}
+
+#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
+/* Where the real work is done */
+int lin_resample_resample(struct resample_data *data, struct dsp_buffer *src,
+ struct dsp_buffer *dst)
+{
+ int ch = src->format.num_channels - 1;
+ uint32_t count = MIN(src->remcount, 0x8000);
+ uint32_t delta = data->delta;
+ uint32_t phase, pos;
+ int32_t *d;
+
+ do
+ {
+ const int32_t *s = src->p32[ch];
+
+ d = dst->p32[ch];
+ int32_t *dmax = d + dst->bufcount;
+
+ phase = data->phase;
+ pos = phase >> 16;
+ pos = MIN(pos, count);
+
+ int32_t last = pos > 0 ? s[pos - 1] : data->last_sample[ch];
+
+ if (pos < count)
+ {
+ while (1)
+ {
+ *d++ = last + FRACMUL((phase & 0xffff) << 15, s[pos] - last);
+ phase += delta;
+ pos = phase >> 16;
+
+ if (pos >= count || d >= dmax)
+ break;
+
+ if (pos > 0)
+ last = s[pos - 1];
+ }
+
+ if (pos > 0)
+ {
+ pos = MIN(pos, count);
+ last = s[pos - 1];
+ }
+ }
+
+ data->last_sample[ch] = last;
+ }
+ while (--ch >= 0);
+
+ /* Wrap phase accumulator back to start of next frame. */
+ data->phase = phase - (pos << 16);
+
+ dst->remcount = d - dst->p32[0];
+
+ return pos;
+}
+#endif /* CPU */
+
+/* Resample count stereo samples or stop when the destination is full.
+ * Updates the src buffer and changes to its own output buffer to refer to
+ * the resampled data. */
+static void lin_resample_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ struct resample_data *data = (void *)this->data;
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &data->resample_buf;
+
+ *buf_p = dst;
+
+ if (dst->remcount > 0)
+ return; /* data still remains */
+
+ int channels = src->format.num_channels;
+
+ dst->remcount = 0;
+ dst->p32[0] = data->resample_buf_arr[0];
+ dst->p32[1] = data->resample_buf_arr[channels - 1];
+
+ if (src->remcount > 0)
+ {
+ dst->bufcount = RESAMPLE_BUF_COUNT;
+
+ int consumed = lin_resample_resample(data, src, dst);
+
+ /* Advance src by consumed amount */
+ if (consumed > 0)
+ dsp_advance_buffer32(src, consumed);
+ }
+ /* else purged resample_buf */
+
+ /* Inherit in-place processed mask from source buffer */
+ dst->proc_mask = src->proc_mask;
+}
+
+/* Finish draining old samples then switch format or shut off */
+static void lin_resample_new_format(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ struct resample_data *data = (void *)this->data;
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &data->resample_buf;
+
+ if (dst->remcount > 0)
+ {
+ *buf_p = dst;
+ return; /* data still remains */
+ }
+
+ DSP_PRINT_FORMAT(DSP_PROC_RESAMPLE, DSP_PROC_RESAMPLE, src->format);
+
+ struct dsp_config *dsp = data->dsp;
+ int32_t frequency = data->frequency;
+ bool active = dsp_proc_active(dsp, DSP_PROC_RESAMPLE);
+
+ if (src->format.frequency != frequency)
+ {
+ DEBUGF(" DSP_PROC_RESAMPLE- new delta\n");
+ active = lin_resample_new_delta(data, src);
+ dsp_proc_activate(dsp, DSP_PROC_RESAMPLE, active);
+ }
+
+ /* Everything after us is NATIVE_FREQUENCY */
+ struct sample_format f = src->format;
+ f.frequency = NATIVE_FREQUENCY;
+ f.codec_frequency = NATIVE_FREQUENCY;
+
+ if (!active)
+ {
+ DEBUGF(" DSP_PROC_RESAMPLE- not active\n");
+ dst->format = f; /* Keep track */
+ return; /* No resampling required */
+ }
+
+ format_change_ack(&src->format);
+
+ if (EQU_SAMPLE_FORMAT(f, dst->format))
+ {
+ DEBUGF(" DSP_PROC_RESAMPLE- same dst format\n");
+ format_change_ack(&f); /* Nothing changed that matters downstream */
+ }
+
+ dst->format = f;
+ dsp_proc_call(this, buf_p, 0);
+}
+
+/* DSP message hook */
+static intptr_t lin_resample_configure(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned int setting,
+ intptr_t value)
+{
+ switch (setting)
+ {
+ case DSP_INIT:
+ /* Always enable resampler so that format changes may be monitored and
+ * it self-activated when required */
+ dsp_proc_enable(dsp, DSP_PROC_RESAMPLE, true);
+ break;
+
+ case DSP_FLUSH:
+ lin_resample_flush(this);
+ break;
+
+ case DSP_PROC_INIT:
+ this->data = (intptr_t)&resample_data[dsp_get_id(dsp)];
+ this->ip_mask = 0; /* Not in-place */
+ this->process[0] = lin_resample_process;
+ this->process[1] = lin_resample_new_format;
+ ((struct resample_data *)this->data)->dsp = dsp;
+ break;
+
+ case DSP_PROC_CLOSE:
+ /* This stage should be enabled at all times */
+ DEBUGF("DSP_PROC_RESAMPLE- Error: Closing!\n");
+ break;
+ }
+
+ return 1;
+ (void)value;
+}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(RESAMPLE,
+ lin_resample_configure);
diff --git a/lib/rbcodec/dsp/pga.c b/lib/rbcodec/dsp/pga.c
new file mode 100644
index 0000000000..c2c29ccfc0
--- /dev/null
+++ b/lib/rbcodec/dsp/pga.c
@@ -0,0 +1,144 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Magnus Holmgren
+ * 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 "dsp.h"
+#include "dsp-util.h"
+#include "fixedpoint.h"
+#include "fracmul.h"
+#include "dsp_proc_entry.h"
+
+/* Implemented here or in target assembly code */
+void pga_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p);
+
+#define DEFAULT_PGA_GAIN (PGA_UNITY >> 1) /* s8.23 format */
+
+static struct pga_data
+{
+ int32_t gain; /* 00h: Final gain in s8.23 format */
+ uint32_t enabled; /* Mask of enabled gains */
+ int32_t gains[PGA_NUM_GAINS]; /* Individual gains in s7.24 format */
+} pga_data =
+{
+ .gain = DEFAULT_PGA_GAIN,
+ .enabled = 0,
+ .gains[0 ... PGA_NUM_GAINS-1] = PGA_UNITY,
+};
+
+/* Combine all gains to a global gain and enable/disable the amplifier if
+ the overall gain is not unity/unity */
+static void pga_update(void)
+{
+ int32_t gain = PGA_UNITY;
+
+ /* Multiply all gains with one another to get overall amp gain */
+ for (int i = 0; i < PGA_NUM_GAINS; i++)
+ {
+ if (pga_data.enabled & BIT_N(i)) /* Only enabled gains factor in */
+ gain = fp_mul(gain, pga_data.gains[i], 24);
+ }
+
+ gain >>= 1; /* s7.24 -> s8.23 format */
+
+ if (gain == pga_data.gain)
+ return;
+
+ struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
+ pga_data.gain = gain;
+ dsp_proc_enable(dsp, DSP_PROC_PGA, gain != DEFAULT_PGA_GAIN);
+ dsp_proc_activate(dsp, DSP_PROC_PGA, true);
+}
+
+
+/** Amp controls **/
+
+/* Set a particular gain value - doesn't have to be enabled */
+void pga_set_gain(enum pga_gain_ids id, int32_t value)
+{
+ if (value == pga_data.gains[id])
+ return;
+
+ pga_data.gains[id] = value;
+
+ if (BIT_N(id) & pga_data.enabled)
+ pga_update();
+}
+
+/* Enable or disable the specified gain stage */
+void pga_enable_gain(enum pga_gain_ids id, bool enable)
+{
+ uint32_t bit = BIT_N(id);
+
+ if (enable != !(pga_data.enabled & bit))
+ return;
+
+ pga_data.enabled ^= bit;
+ pga_update();
+}
+
+
+/** DSP interface **/
+
+#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
+/* Apply a constant gain to the samples (e.g., for ReplayGain). */
+void pga_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
+{
+ int32_t gain = ((struct pga_data *)this->data)->gain;
+ struct dsp_buffer *buf = *buf_p;
+ unsigned int channels = buf->format.num_channels;
+
+ for (unsigned int ch = 0; ch < channels; ch++)
+ {
+ int32_t *d = buf->p32[ch];
+ int count = buf->remcount;
+
+ for (int i = 0; i < count; i++)
+ d[i] = FRACMUL_SHL(d[i], gain, 8);
+ }
+
+ (void)this;
+}
+#endif /* CPU */
+
+/* DSP message hook */
+static intptr_t pga_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)
+ break; /* Already initialized */
+ this->data = (intptr_t)&pga_data;
+ this->process[0] = pga_process;
+ break;
+ }
+
+ return 1;
+ (void)dsp;
+}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(PGA,
+ pga_configure);
diff --git a/lib/rbcodec/dsp/pga.h b/lib/rbcodec/dsp/pga.h
new file mode 100644
index 0000000000..f0c4c4717f
--- /dev/null
+++ b/lib/rbcodec/dsp/pga.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PGA_H
+#define PGA_H
+
+#define PGA_UNITY ((int32_t)0x01000000) /* s7.24 */
+
+/* Various gains supported by pre-gain amp */
+enum pga_gain_ids
+{
+ PGA_EQ_PRECUT = 0,
+ PGA_REPLAYGAIN,
+#ifdef HAVE_SW_VOLUME_CONTROL
+ PGA_VOLUME,
+#endif
+ PGA_NUM_GAINS,
+};
+
+void pga_set_gain(enum pga_gain_ids id, int32_t value);
+void pga_enable_gain(enum pga_gain_ids id, bool enable);
+
+#endif /* PGA_H */
diff --git a/lib/rbcodec/dsp/tdspeed.c b/lib/rbcodec/dsp/tdspeed.c
index c2f4a3f704..3aa8acc458 100644
--- a/lib/rbcodec/dsp/tdspeed.c
+++ b/lib/rbcodec/dsp/tdspeed.c
@@ -9,6 +9,7 @@
*
* Copyright (C) 2006 by Nicolas Pitre <nico@cam.org>
* Copyright (C) 2006-2007 by St├ęphane Doyon <s.doyon@videotron.ca>
+ * 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
@@ -19,69 +20,42 @@
* KIND, either express or implied.
*
****************************************************************************/
-
-#include <inttypes.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
+#include "config.h"
+#include "system.h"
#include "sound.h"
#include "core_alloc.h"
#include "system.h"
#include "tdspeed.h"
#include "settings.h"
#include "dsp-util.h"
+#include "dsp_proc_entry.h"
#define assert(cond)
+#define TIMESTRETCH_SET_FACTOR (DSP_PROC_SETTING+DSP_PROC_TIMESTRETCH)
+
#define MIN_RATE 8000
#define MAX_RATE 48000 /* double buffer for double rate */
#define MINFREQ 100
-#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */
-
-static int32_t** dsp_src;
-static int handles[4];
-static int32_t *overlap_buffer[2] = { NULL, NULL };
-static int32_t *outbuf[2] = { NULL, NULL };
-
-static int move_callback(int handle, void* current, void* new)
-{
- /* TODO */
- (void)handle;
- if (dsp_src)
- {
- int ch = (current == outbuf[0]) ? 0 : 1;
- dsp_src[ch] = outbuf[ch] = new;
- }
- return BUFLIB_CB_OK;
-}
-
-static struct buflib_callbacks ops = {
- .move_callback = move_callback,
- .shrink_callback = NULL,
-};
+#define MAX_INPUTCOUNT 512 /* Max input count so dst doesn't overflow */
+#define FIXED_BUFCOUNT 3072 /* 48KHz factor 3.0 */
+#define FIXED_OUTBUFCOUNT 4096
-static int ovl_move_callback(int handle, void* current, void* new)
+enum tdspeed_ops
{
- /* TODO */
- (void)handle;
- if (dsp_src)
- {
- int ch = (current == overlap_buffer[0]) ? 0 : 1;
- overlap_buffer[ch] = new;
- }
- return BUFLIB_CB_OK;
-}
-
-static struct buflib_callbacks ovl_ops = {
- .move_callback = ovl_move_callback,
- .shrink_callback = NULL,
+ TDSOP_PROCESS,
+ TDSOP_LAST,
+ TDSOP_PURGE,
};
-
static struct tdspeed_state_s
{
- bool stereo;
+ struct dsp_proc_entry *this; /* this stage */
+ struct dsp_config *dsp; /* the DSP we use */
+ unsigned int channels; /* flags parameter to use in call */
+ int32_t samplerate; /* current samplerate of input data */
+ int32_t factor; /* stretch factor (perdecimille) */
int32_t shift_max; /* maximum displacement on a frame */
int32_t src_step; /* source window pace */
int32_t dst_step; /* destination window pace */
@@ -89,62 +63,132 @@ static struct tdspeed_state_s
int32_t ovl_shift; /* overlap buffer frame shift */
int32_t ovl_size; /* overlap buffer used size */
int32_t ovl_space; /* overlap buffer size */
- int32_t *ovl_buff[2]; /* overlap buffer */
+ int32_t *ovl_buff[2]; /* overlap buffer (L+R) */
} tdspeed_state;
-void tdspeed_init(void)
+static int handles[4] = { 0, 0, 0, 0 };
+static int32_t *buffers[4] = { NULL, NULL, NULL, NULL };
+
+#define overlap_buffer (&buffers[0])
+#define outbuf (&buffers[2])
+#define out_size FIXED_OUTBUFCOUNT
+
+/* Processed buffer passed out to later stages */
+static struct dsp_buffer dsp_outbuf;
+
+static int move_callback(int handle, void *current, void *new)
{
- if (!global_settings.timestretch_enabled)
- return;
+#if 0
+ /* Should not currently need to block this since DSP loop completes an
+ iteration before yielding and begins again at its input buffer */
+ if (dsp_is_busy(tdspeed_state.dsp))
+ return BUFLIB_CB_CANNOT_MOVE; /* DSP processing in progress */
+#endif
- /* Allocate buffers */
- if (overlap_buffer[0] == NULL)
+ ptrdiff_t shift = (int32_t *)new - (int32_t *)current;
+ int32_t **p32 = dsp_outbuf.p32;
+
+ for (unsigned int i = 0; i < ARRAYLEN(handles); i++)
{
- handles[0] = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
- overlap_buffer[0] = core_get_data(handles[0]);
+ if (handle != handles[i])
+ continue;
+
+ switch (i)
+ {
+ case 0: case 1:
+ /* moving overlap (input) buffers */
+ tdspeed_state.ovl_buff[i] = new;
+ break;
+
+ case 2:
+ /* moving outbuf left channel and dsp_outbuf.p32[0] */
+ if (p32[0] == p32[1])
+ p32[1] += shift; /* mono mode */
+
+ p32[0] += shift;
+ break;
+
+ case 3:
+ /* moving outbuf right channel and dsp_outbuf.p32[1] */
+ p32[1] += shift;
+ break;
+ }
+
+ buffers[i] = new;
+ break;
}
- if (overlap_buffer[1] == NULL)
+
+ return BUFLIB_CB_OK;
+}
+
+static struct buflib_callbacks ops =
+{
+ .move_callback = move_callback,
+ .shrink_callback = NULL,
+};
+
+/* Allocate timestretch buffers */
+static bool tdspeed_alloc_buffers(void)
+{
+ static const struct
{
- handles[1] = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
- overlap_buffer[1] = core_get_data(handles[1]);
- }
- if (outbuf[0] == NULL)
+ const char *name;
+ size_t size;
+ } bufdefs[4] =
{
- handles[2] = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
- outbuf[0] = core_get_data(handles[2]);
- }
- if (outbuf[1] == NULL)
+ { "tdspeed ovl L", FIXED_BUFCOUNT * sizeof(int32_t) },
+ { "tdspeed ovl R", FIXED_BUFCOUNT * sizeof(int32_t) },
+ { "tdspeed out L", FIXED_OUTBUFCOUNT * sizeof(int32_t) },
+ { "tdspeed out R", FIXED_OUTBUFCOUNT * sizeof(int32_t) },
+ };
+
+ for (unsigned int i = 0; i < ARRAYLEN(bufdefs); i++)
{
- handles[3] = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
- outbuf[1] = core_get_data(handles[3]);
+ if (handles[i] <= 0)
+ {
+ handles[i] = core_alloc_ex(bufdefs[i].name, bufdefs[i].size, &ops);
+
+ if (handles[i] <= 0)
+ return false;
+ }
+
+ if (buffers[i] == NULL)
+ {
+ buffers[i] = core_get_data(handles[i]);
+
+ if (buffers[i] == NULL)
+ return false;
+ }
}
+
+ return true;
}
-void tdspeed_finish(void)
+/* Free timestretch buffers */
+static void tdspeed_free_buffers(void)
{
- for(unsigned i = 0; i < ARRAYLEN(handles); i++)
+ for (unsigned int i = 0; i < ARRAYLEN(handles); i++)
{
if (handles[i] > 0)
- {
core_free(handles[i]);
- handles[i] = 0;
- }
+
+ handles[i] = 0;
+ buffers[i] = NULL;
}
- overlap_buffer[0] = overlap_buffer[1] = NULL;
- outbuf[0] = outbuf[1] = NULL;
}
-bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
+/* Discard all data */
+static void tdspeed_flush(void)
{
struct tdspeed_state_s *st = &tdspeed_state;
- int src_frame_sz;
-
- /* Check buffers were allocated ok */
- if (overlap_buffer[0] == NULL || overlap_buffer[1] == NULL)
- return false;
+ st->ovl_size = 0;
+ st->ovl_shift = 0;
+ dsp_outbuf.remcount = 0; /* Dump remaining output */
+}
- if (outbuf[0] == NULL || outbuf[1] == NULL)
- return false;
+static bool tdspeed_update(int32_t samplerate, int32_t factor)
+{
+ struct tdspeed_state_s *st = &tdspeed_state;
/* Check parameters */
if (factor == PITCH_SPEED_100)
@@ -156,7 +200,10 @@ bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
if (factor < STRETCH_MIN || factor > STRETCH_MAX)
return false;
- st->stereo = stereo;
+ /* Save parameters we'll need later if format changes */
+ st->samplerate = samplerate;
+ st->factor = factor;
+
st->dst_step = samplerate / MINFREQ;
if (factor > PITCH_SPEED_100)
@@ -171,7 +218,7 @@ bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
st->src_step = st->dst_step * factor / PITCH_SPEED_100;
st->shift_max = (st->dst_step > st->src_step) ? st->dst_step : st->src_step;
- src_frame_sz = st->shift_max + st->dst_step;
+ int src_frame_sz = st->shift_max + st->dst_step;
if (st->dst_step > st->src_step)
src_frame_sz += st->dst_step - st->src_step;
@@ -182,32 +229,27 @@ bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
if (st->src_step > st->dst_step)
st->ovl_space += 2*st->src_step - st->dst_step;
- if (st->ovl_space > FIXED_BUFSIZE)
- st->ovl_space = FIXED_BUFSIZE;
+ if (st->ovl_space > FIXED_BUFCOUNT)
+ st->ovl_space = FIXED_BUFCOUNT;
+ /* just discard remaining input data */
st->ovl_size = 0;
st->ovl_shift = 0;
st->ovl_buff[0] = overlap_buffer[0];
-
- if (stereo)
- st->ovl_buff[1] = overlap_buffer[1];
- else
- st->ovl_buff[1] = st->ovl_buff[0];
+ st->ovl_buff[1] = overlap_buffer[1]; /* ignored if mono */
return true;
}
static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2],
- int data_len, int last, int out_size)
+ int data_len, enum tdspeed_ops op, int *consumed)
/* data_len in samples */
{
struct tdspeed_state_s *st = &tdspeed_state;
int32_t *dest[2];
int32_t next_frame, prev_frame, src_frame_sz;
- bool stereo = buf_in[0] != buf_in[1];
-
- assert(stereo == st->stereo);
+ bool stereo = st->channels > 1;
src_frame_sz = st->shift_max + st->dst_step;
@@ -233,7 +275,7 @@ static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2],
if (copy > data_len)
copy = data_len;
- assert(st->ovl_size + copy <= FIXED_BUFSIZE);
+ assert(st->ovl_size + copy <= FIXED_BUFCOUNT);
memcpy(st->ovl_buff[0] + st->ovl_size, buf_in[0],
copy * sizeof(int32_t));
@@ -241,7 +283,9 @@ static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2],
memcpy(st->ovl_buff[1] + st->ovl_size, buf_in[1],
copy * sizeof(int32_t));
- if (!last && have + copy < src_frame_sz)
+ *consumed += copy;
+
+ if (op == TDSOP_PROCESS && have + copy < src_frame_sz)
{
/* still not enough to process at least one frame */
st->ovl_size += copy;
@@ -254,13 +298,14 @@ static int tdspeed_apply(int32_t *buf_out[2], int32_t *buf_in[2],
if (copy == data_len)
{
- assert(have + copy <= FIXED_BUFSIZE);
- return tdspeed_apply(buf_out, st->ovl_buff, have+copy, last,
- out_size);
+ assert(have + copy <= FIXED_BUFCOUNT);
+ return tdspeed_apply(buf_out, st->ovl_buff, have+copy, op,
+ consumed);
}
- assert(have + copy <= FIXED_BUFSIZE);
- int i = tdspeed_apply(buf_out, st->ovl_buff, have+copy, -1, out_size);
+ assert(have + copy <= FIXED_BUFCOUNT);
+ int i = tdspeed_apply(buf_out, st->ovl_buff, have+copy,
+ TDSOP_LAST, consumed);
dest[0] = buf_out[0] + i;
dest[1] = buf_out[1] + i;
@@ -379,12 +424,12 @@ skip:;
}
/* now deal with remaining partial frames */
- if (last == -1)
+ if (op == TDSOP_LAST)
{
/* special overlap buffer processing: remember frame shift only */
st->ovl_shift = next_frame - prev_frame;
}
- else if (last != 0)
+ else if (op == TDSOP_PURGE)
{
/* last call: purge all remaining data to output buffer */
int i = data_len - prev_frame;
@@ -400,6 +445,8 @@ skip:;
memcpy(dest[1], buf_in[1] + prev_frame, i * sizeof(int32_t));
dest[1] += i;
}
+
+ *consumed += i;
}
else
{
@@ -408,7 +455,7 @@ skip:;
int i = (st->ovl_shift < 0) ? next_frame : prev_frame;
st->ovl_size = data_len - i;
- assert(st->ovl_size <= FIXED_BUFSIZE);
+ assert(st->ovl_size <= FIXED_BUFCOUNT);
memcpy(st->ovl_buff[0], buf_in[0] + i, st->ovl_size * sizeof(int32_t));
if (stereo)
@@ -418,32 +465,223 @@ skip:;
return dest[0] - buf_out[0];
}
-long tdspeed_est_output_size()
+
+/** DSP interface **/
+
+static void tdspeed_process_new_format(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p);
+
+/* Enable or disable the availability of timestretch */
+void dsp_timestretch_enable(bool enabled)
{
- return TDSPEED_OUTBUFSIZE;
+ if (enabled != !tdspeed_state.this)
+ return; /* No change */
+
+ dsp_proc_enable(dsp_get_config(CODEC_IDX_AUDIO), DSP_PROC_TIMESTRETCH,
+ enabled);
}
-long tdspeed_est_input_size(long size)
+/* Set the timestretch ratio */
+void dsp_set_timestretch(int32_t percent)
{
struct tdspeed_state_s *st = &tdspeed_state;
- size = (size - st->ovl_size) * st->src_step / st->dst_step;
+ if (!st->this)
+ return; /* not enabled */
+
+ if (percent <= 0)
+ percent = PITCH_SPEED_100;
+
+ if (percent == st->factor)
+ return; /* no change */
+
+ dsp_configure(st->dsp, TIMESTRETCH_SET_FACTOR, percent);
+}
+
+/* Return the timestretch ratio */
+int32_t dsp_get_timestretch(void)
+{
+ return tdspeed_state.factor;
+}
+
+/* Return whether or not timestretch is enabled and initialized */
+bool dsp_timestretch_available(void)
+{
+ return !!tdspeed_state.this;
+}
+
+/* Apply timestretch to the input buffer and switch to our output buffer */
+static void tdspeed_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &dsp_outbuf;
+
+ *buf_p = dst; /* switch to our buffer */
+
+ int count = dst->remcount;
+
+ if (count > 0)
+ return; /* output remains from an earlier call */
- if (size < 0)
- size = 0;
+ dst->p32[0] = outbuf[0];
+ dst->p32[1] = outbuf[src->format.num_channels - 1];
- return size;
+ if (src->remcount > 0)
+ {
+ dst->bufcount = 0; /* use this to get consumed src */
+ count = tdspeed_apply(dst->p32, src->p32,
+ MIN(src->remcount, MAX_INPUTCOUNT),
+ TDSOP_PROCESS, &dst->bufcount);
+
+ /* advance src by samples consumed */
+ if (dst->bufcount > 0)
+ dsp_advance_buffer32(src, dst->bufcount);
+ }
+ /* else purged dsp_outbuf */
+
+ dst->remcount = count;
+
+ /* inherit in-place processed mask from source buffer */
+ dst->proc_mask = src->proc_mask;
+
+ (void)this;
}
-int tdspeed_doit(int32_t *src[], int count)
+/* Process format changes and settings changes */
+static void tdspeed_process_new_format(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
{
- dsp_src = src;
- count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] },
- src, count, 0, TDSPEED_OUTBUFSIZE);
+ struct dsp_buffer *src = *buf_p;
+ struct dsp_buffer *dst = &dsp_outbuf;
+
+ if (dst->remcount > 0)
+ {
+ *buf_p = dst;
+ return; /* output remains from an earlier call */
+ }
- src[0] = outbuf[0];
- src[1] = outbuf[1];
+ DSP_PRINT_FORMAT(DSP_PROC_TIMESTRETCH, DSP_PROC_TIMESTRETCH, src->format);
+
+ struct tdspeed_state_s *st = &tdspeed_state;
+ struct dsp_config *dsp = st->dsp;
+ struct sample_format *format = &src->format;
+ unsigned int channels = format->num_channels;
+
+ if (format->codec_frequency != st->samplerate)
+ {
+ /* relevent parameters are changing - all overlap will be discarded */
+ st->channels = channels;
+
+ DEBUGF(" DSP_PROC_TIMESTRETCH- new settings: "
+ "ch:%u chz: %u, %d.%02d%%\n",
+ channels,
+ format->codec_frequency,
+ st->factor / 100, st->factor % 100);
+ bool active = tdspeed_update(format->codec_frequency, st->factor);
+ dsp_proc_activate(dsp, DSP_PROC_TIMESTRETCH, active);
+
+ if (!active)
+ {
+ DEBUGF(" DSP_PROC_RESAMPLE- not active\n");
+ dst->format = src->format; /* Keep track */
+ return; /* no more for now */
+ }
+ }
+ else if (channels != st->channels)
+ {
+ /* channel count transistion - have to make old data in overlap
+ buffer compatible with new format */
+ DEBUGF(" DSP_PROC_TIMESTRETCH- new ch count: %u=>%u\n",
+ st->channels, channels);
+
+ st->channels = channels;
+
+ if (channels > 1)
+ {
+ /* mono->stereo: Process the old mono as stereo now */
+ memcpy(st->ovl_buff[1], st->ovl_buff[0],
+ st->ovl_size * sizeof (int32_t));
+ }
+ else
+ {
+ /* stereo->mono: Process the old stereo as mono now */
+ for (int i = 0; i < st->ovl_size; i++)
+ {
+ st->ovl_buff[0][i] = st->ovl_buff[0][i] / 2 +
+ st->ovl_buff[1][i] / 2;
+ }
+ }
+ }
+
+ struct sample_format f = *format;
+ format_change_ack(format);
+
+ if (EQU_SAMPLE_FORMAT(f, dst->format))
+ {
+ DEBUGF(" DSP_PROC_TIMESTRETCH- same dst format\n");
+ format_change_ack(&f); /* nothing changed that matters downstream */
+ }
+
+ dst->format = f;
+
+ /* return to normal processing */
+ this->process[0] = tdspeed_process;
+ dsp_proc_call(this, buf_p, 0);
+}
+
+/* DSP message hook */
+static intptr_t tdspeed_configure(struct dsp_proc_entry *this,
+ struct dsp_config *dsp,
+ unsigned int setting,
+ intptr_t value)
+{
+ struct tdspeed_state_s *st = &tdspeed_state;
+
+ switch (setting)
+ {
+ case DSP_INIT:
+ /* everything is at 100% until dsp_set_timestretch is called with
+ some other value and timestretch is enabled at the time */
+ if (value == CODEC_IDX_AUDIO)
+ st->factor = PITCH_SPEED_100;
+ break;
+
+ case DSP_FLUSH:
+ tdspeed_flush();
+ break;
+
+ case DSP_PROC_INIT:
+ if (!tdspeed_alloc_buffers())
+ return -1; /* fail the init */
+
+ st->this = this;
+ st->dsp = dsp;
+ this->ip_mask = 0; /* not in-place */
+ this->process[0] = tdspeed_process;
+ this->process[1] = tdspeed_process_new_format;
+ break;
+
+ case DSP_PROC_CLOSE:
+ st->this = NULL;
+ st->factor = PITCH_SPEED_100;
+ dsp_outbuf.remcount = 0;
+ tdspeed_free_buffers();
+ break;
+
+ case TIMESTRETCH_SET_FACTOR:
+ /* force update as a format change */
+ st->samplerate = 0;
+ st->factor = (int32_t)value;
+ st->this->process[0] = tdspeed_process_new_format;
+ dsp_proc_activate(st->dsp, DSP_PROC_TIMESTRETCH, true);
+ break;
+ }
- return count;
+ return 1;
+ (void)value;
}
+/* Database entry */
+DSP_PROC_DB_ENTRY(TIMESTRETCH,
+ tdspeed_configure);
diff --git a/lib/rbcodec/dsp/tdspeed.h b/lib/rbcodec/dsp/tdspeed.h
index e91eeb1701..ca8a7846a4 100644
--- a/lib/rbcodec/dsp/tdspeed.h
+++ b/lib/rbcodec/dsp/tdspeed.h
@@ -23,12 +23,8 @@
#ifndef _TDSPEED_H
#define _TDSPEED_H
-#include "dsp.h"
-
-#define TDSPEED_OUTBUFSIZE 4096
-
-/* some #define functions to get the pitch, stretch and speed values based on */
-/* two known values. Remember that params are alphabetical. */
+/* some #define functions to get the pitch, stretch and speed values based
+ * upon two known values. Remember that params are alphabetical. */
#define GET_SPEED(pitch, stretch) \
((pitch * stretch + PITCH_SPEED_100 / 2L) / PITCH_SPEED_100)
#define GET_PITCH(speed, stretch) \
@@ -36,14 +32,12 @@
#define GET_STRETCH(pitch, speed) \
((speed * PITCH_SPEED_100 + pitch / 2L) / pitch)
-void tdspeed_init(void);
-void tdspeed_finish(void);
-bool tdspeed_config(int samplerate, bool stereo, int32_t factor);
-long tdspeed_est_output_size(void);
-long tdspeed_est_input_size(long size);
-int tdspeed_doit(int32_t *src[], int count);
-
#define STRETCH_MAX (250L * PITCH_SPEED_PRECISION) /* 250% */
#define STRETCH_MIN (35L * PITCH_SPEED_PRECISION) /* 35% */
-#endif
+void dsp_timestretch_enable(bool enable);
+void dsp_set_timestretch(int32_t percent);
+int32_t dsp_get_timestretch(void);
+bool dsp_timestretch_available(void);
+
+#endif /* _TDSPEED_H */
diff --git a/lib/rbcodec/dsp/tone_controls.c b/lib/rbcodec/dsp/tone_controls.c
new file mode 100644
index 0000000000..0bd4a447d7
--- /dev/null
+++ b/lib/rbcodec/dsp/tone_controls.c
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 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 "dsp.h"
+#include <string.h>
+#include "dsp_proc_entry.h"
+#include "dsp_filter.h"
+
+/* These apply to all DSP streams to remain as consistant as possible with
+ * the behavior of hardware tone controls */
+
+/* Cutoffs in HZ - not adjustable for now */
+static const unsigned int tone_bass_cutoff = 200;
+static const unsigned int tone_treble_cutoff = 3500;
+
+/* Current bass and treble gain values */
+static int tone_bass = 0;
+static int tone_treble = 0;
+
+/* Data for each DSP */
+static struct dsp_filter tone_filters[DSP_COUNT] IBSS_ATTR;
+
+/* Update the filters' coefficients based upon the bass/treble settings */
+void tone_set_prescale(int prescale)
+{
+ int bass = tone_bass;
+ int treble = tone_treble;
+
+ struct dsp_filter tone_filter; /* Temp to hold new version */
+ filter_bishelf_coefs(0xffffffff / NATIVE_FREQUENCY * tone_bass_cutoff,
+ 0xffffffff / NATIVE_FREQUENCY * tone_treble_cutoff,
+ bass, treble, -prescale, &tone_filter);
+
+ struct dsp_config *dsp;
+ for (int i = 0; (dsp = dsp_get_config(i)); i++)
+ {
+ struct dsp_filter *filter = &tone_filters[i];
+ filter_copy(filter, &tone_filter);
+
+ bool enable = bass != 0 || treble != 0;
+ dsp_proc_enable(dsp, DSP_PROC_TONE_CONTROLS, enable);
+
+ if (!dsp_proc_active(dsp, DSP_PROC_TONE_CONTROLS))
+ {
+ filter_flush(filter); /* Going online */
+ dsp_proc_activate(dsp, DSP_PROC_TONE_CONTROLS, true);
+ }
+ }
+}
+
+/* Prescaler is always set after setting bass/treble, so we wait with
+ * calculating coefs until such time. */
+
+/* Change the bass setting */
+void tone_set_bass(int bass)
+{
+ tone_bass = bass;
+}
+
+/* Change the treble setting */
+void tone_set_treble(int treble)
+{
+ tone_treble = treble;
+}
+
+/* Apply the tone control filter in-place */
+static void tone_process(struct dsp_proc_entry *this,
+ struct dsp_buffer **buf_p)
+{
+ struct dsp_buffer *buf = *buf_p;
+ filter_process((void *)this->data, buf->p32, buf->remcount,
+ buf->format.num_channels);
+}
+
+/* DSP message hook */
+static intptr_t tone_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)
+ break;
+ this->data = (intptr_t)&tone_filters[dsp_get_id(dsp)];
+ this->process[0] = tone_process;
+ case DSP_FLUSH:
+ filter_flush((struct dsp_filter *)this->data);
+ break;
+ }
+
+ return 1;
+}
+
+/* Database entry */
+DSP_PROC_DB_ENTRY(TONE_CONTROLS,
+ tone_configure);
diff --git a/lib/rbcodec/dsp/tone_controls.h b/lib/rbcodec/dsp/tone_controls.h
new file mode 100644
index 0000000000..1e27ecf800
--- /dev/null
+++ b/lib/rbcodec/dsp/tone_controls.h
@@ -0,0 +1,28 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Thom Johansen
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef TONE_CONTROLS_H
+#define TONE_CONTROLS_H
+
+void tone_set_prescale(int prescale);
+void tone_set_bass(int bass);
+void tone_set_treble(int treble);
+
+#endif /* TONE_CONTROLS_H */