summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/beep.c13
-rw-r--r--apps/codec_thread.c1
-rw-r--r--apps/features.txt4
-rw-r--r--apps/lang/english.lang17
-rw-r--r--apps/menus/playback_menu.c27
-rw-r--r--apps/pcmbuf.c21
-rw-r--r--apps/pcmbuf.h2
-rw-r--r--apps/playback.c11
-rw-r--r--apps/plugin.c2
-rw-r--r--apps/plugin.h4
-rw-r--r--apps/plugins/SOURCES2
-rw-r--r--apps/plugins/mpegplayer/audio_thread.c1
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.h2
-rw-r--r--apps/plugins/mpegplayer/pcm_output.c6
-rw-r--r--apps/plugins/oscilloscope.c19
-rw-r--r--apps/plugins/test_codec.c7
-rw-r--r--apps/rbcodecconfig.h4
-rw-r--r--apps/settings.c38
-rw-r--r--apps/settings.h7
-rw-r--r--apps/settings_list.c5
-rw-r--r--apps/voice_thread.c11
-rw-r--r--firmware/export/config_caps.h34
-rw-r--r--firmware/export/pcm.h3
-rw-r--r--firmware/export/pcm_mixer.h6
-rw-r--r--firmware/export/pcm_sampr.h56
-rw-r--r--firmware/pcm.c6
-rw-r--r--firmware/pcm_mixer.c27
-rw-r--r--lib/rbcodec/dsp/compressor.c18
-rw-r--r--lib/rbcodec/dsp/crossfeed.c117
-rw-r--r--lib/rbcodec/dsp/dsp_arm.S84
-rw-r--r--lib/rbcodec/dsp/dsp_cf.S102
-rw-r--r--lib/rbcodec/dsp/dsp_core.c29
-rw-r--r--lib/rbcodec/dsp/dsp_core.h4
-rw-r--r--lib/rbcodec/dsp/dsp_misc.c15
-rw-r--r--lib/rbcodec/dsp/dsp_misc.h7
-rw-r--r--lib/rbcodec/dsp/dsp_proc_database.h2
-rw-r--r--lib/rbcodec/dsp/dsp_proc_entry.h4
-rw-r--r--lib/rbcodec/dsp/dsp_sample_input.c30
-rw-r--r--lib/rbcodec/dsp/dsp_sample_io.h5
-rw-r--r--lib/rbcodec/dsp/eq.c77
-rw-r--r--lib/rbcodec/dsp/resample.c41
-rw-r--r--lib/rbcodec/dsp/tone_controls.c29
-rw-r--r--lib/rbcodec/rbcodecconfig-example.h4
-rw-r--r--lib/rbcodec/test/warble.c5
44 files changed, 678 insertions, 231 deletions
diff --git a/apps/beep.c b/apps/beep.c
index 8ac7ccf224..25b5e0e391 100644
--- a/apps/beep.c
+++ b/apps/beep.c
@@ -21,10 +21,10 @@
21#include "config.h" 21#include "config.h"
22#include "system.h" 22#include "system.h"
23#include "settings.h" 23#include "settings.h"
24#include "dsp_core.h" /* for NATIVE_FREQUENCY */
25#include "pcm.h" 24#include "pcm.h"
26#include "pcm_mixer.h" 25#include "pcm_mixer.h"
27#include "misc.h" 26#include "misc.h"
27#include "fixedpoint.h"
28 28
29/** Beep generation, CPU optimized **/ 29/** Beep generation, CPU optimized **/
30#include "asm/beep.c" 30#include "asm/beep.c"
@@ -39,8 +39,10 @@ static uint32_t beep_amplitude; /* Amplitude of square wave generator */
39#endif 39#endif
40static int beep_count; /* Number of samples remaining to generate */ 40static int beep_count; /* Number of samples remaining to generate */
41 41
42/* Reserve enough static space for keyclick to fit */ 42#define BEEP_COUNT(fs, duration) ((fs) / 1000 * (duration))
43#define BEEP_BUF_COUNT (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION) 43
44/* Reserve enough static space for keyclick to fit in worst case */
45#define BEEP_BUF_COUNT BEEP_COUNT(PLAY_SAMPR_MAX, KEYCLICK_DURATION)
44static int16_t beep_buf[BEEP_BUF_COUNT*2] IBSS_ATTR __attribute__((aligned(4))); 46static int16_t beep_buf[BEEP_BUF_COUNT*2] IBSS_ATTR __attribute__((aligned(4)));
45 47
46/* Callback to generate the beep frames - also don't want inlining of 48/* Callback to generate the beep frames - also don't want inlining of
@@ -75,9 +77,10 @@ void beep_play(unsigned int frequency, unsigned int duration,
75 amplitude = INT16_MAX; 77 amplitude = INT16_MAX;
76 78
77 /* Setup the parameters for the square wave generator */ 79 /* Setup the parameters for the square wave generator */
80 uint32_t fout = mixer_get_frequency();
78 beep_phase = 0; 81 beep_phase = 0;
79 beep_step = 0xffffffffu / NATIVE_FREQUENCY * frequency; 82 beep_step = fp_div(frequency, fout, 32);
80 beep_count = NATIVE_FREQUENCY / 1000 * duration; 83 beep_count = BEEP_COUNT(fout, duration);
81 84
82#ifdef BEEP_GENERIC 85#ifdef BEEP_GENERIC
83 beep_amplitude = amplitude; 86 beep_amplitude = amplitude;
diff --git a/apps/codec_thread.c b/apps/codec_thread.c
index d4b1c64573..8f9f5a3c74 100644
--- a/apps/codec_thread.c
+++ b/apps/codec_thread.c
@@ -507,6 +507,7 @@ static void run_codec(void)
507 codec_queue_ack(Q_CODEC_RUN); 507 codec_queue_ack(Q_CODEC_RUN);
508 508
509 trigger_cpu_boost(); 509 trigger_cpu_boost();
510 dsp_configure(ci.dsp, DSP_SET_OUT_FREQUENCY, pcmbuf_get_frequency());
510 511
511 if (!encoder) 512 if (!encoder)
512 { 513 {
diff --git a/apps/features.txt b/apps/features.txt
index a65744fd5a..897657f52c 100644
--- a/apps/features.txt
+++ b/apps/features.txt
@@ -274,3 +274,7 @@ lowmem
274#if defined(HAVE_HARDWARE_CLICK) 274#if defined(HAVE_HARDWARE_CLICK)
275hardware_click 275hardware_click
276#endif 276#endif
277
278#if defined(HAVE_PLAY_FREQ)
279play_frequency
280#endif
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index dbd0baa18a..0dd3b43f5a 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -13156,3 +13156,20 @@
13156 *: "Slow" 13156 *: "Slow"
13157 </voice> 13157 </voice>
13158</phrase> 13158</phrase>
13159<phrase>
13160 id: LANG_PLAYBACK_FREQUENCY
13161 desc: in playback settings (merge with LANG_RECORDING_FREQUENCY if cleaning)
13162 user: core
13163 <source>
13164 *: none
13165 play_frequency: "Frequency"
13166 </source>
13167 <dest>
13168 *: none
13169 play_frequency: "Frequency"
13170 </dest>
13171 <voice>
13172 *: none
13173 play_frequency: "Frequency"
13174 </voice>
13175</phrase>
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index 6beda93991..89472d45b9 100644
--- a/apps/menus/playback_menu.c
+++ b/apps/menus/playback_menu.c
@@ -37,6 +37,10 @@
37#include "misc.h" 37#include "misc.h"
38#if CONFIG_CODEC == SWCODEC 38#if CONFIG_CODEC == SWCODEC
39#include "playback.h" 39#include "playback.h"
40#include "pcm_sampr.h"
41#ifdef HAVE_PLAY_FREQ
42#include "talk.h"
43#endif
40#endif 44#endif
41 45
42 46
@@ -192,6 +196,10 @@ MENUITEM_SETTING(prevent_skip, &global_settings.prevent_skip, NULL);
192MENUITEM_SETTING(resume_rewind, &global_settings.resume_rewind, NULL); 196MENUITEM_SETTING(resume_rewind, &global_settings.resume_rewind, NULL);
193#endif 197#endif
194MENUITEM_SETTING(pause_rewind, &global_settings.pause_rewind, NULL); 198MENUITEM_SETTING(pause_rewind, &global_settings.pause_rewind, NULL);
199#ifdef HAVE_PLAY_FREQ
200MENUITEM_SETTING(play_frequency, &global_settings.play_frequency,
201 playback_callback);
202#endif
195 203
196MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0, 204MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
197 Icon_Playback_menu, 205 Icon_Playback_menu,
@@ -217,12 +225,15 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
217#ifdef HAVE_HEADPHONE_DETECTION 225#ifdef HAVE_HEADPHONE_DETECTION
218 ,&unplug_menu 226 ,&unplug_menu
219#endif 227#endif
220 ,&skip_length, &prevent_skip, 228 ,&skip_length, &prevent_skip
221 229
222#if CONFIG_CODEC == SWCODEC 230#if CONFIG_CODEC == SWCODEC
223 &resume_rewind, 231 ,&resume_rewind
232#endif
233 ,&pause_rewind
234#ifdef HAVE_PLAY_FREQ
235 ,&play_frequency
224#endif 236#endif
225 &pause_rewind,
226 ); 237 );
227 238
228static int playback_callback(int action,const struct menu_item_ex *this_item) 239static int playback_callback(int action,const struct menu_item_ex *this_item)
@@ -243,9 +254,19 @@ static int playback_callback(int action,const struct menu_item_ex *this_item)
243 break; 254 break;
244 255
245 case ACTION_EXIT_MENUITEM: /* on exit */ 256 case ACTION_EXIT_MENUITEM: /* on exit */
257 /* Playing or not */
258#ifdef HAVE_PLAY_FREQ
259 if (this_item == &play_frequency)
260 {
261 settings_apply_play_freq(global_settings.play_frequency, false);
262 break;
263 }
264#endif /* HAVE_PLAY_FREQ */
265
246 if (!(audio_status() & AUDIO_STATUS_PLAY)) 266 if (!(audio_status() & AUDIO_STATUS_PLAY))
247 break; 267 break;
248 268
269 /* Playing only */
249 if (this_item == &shuffle_item) 270 if (this_item == &shuffle_item)
250 { 271 {
251 if (old_shuffle == global_settings.playlist_shuffle) 272 if (old_shuffle == global_settings.playlist_shuffle)
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index cc454a49ce..ff9b3e16a2 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -40,7 +40,6 @@
40#include "settings.h" 40#include "settings.h"
41#include "audio.h" 41#include "audio.h"
42#include "voice_thread.h" 42#include "voice_thread.h"
43#include "dsp_core.h"
44 43
45/* This is the target fill size of chunks on the pcm buffer 44/* This is the target fill size of chunks on the pcm buffer
46 Can be any number of samples but power of two sizes make for faster and 45 Can be any number of samples but power of two sizes make for faster and
@@ -66,11 +65,11 @@
66 chunks */ 65 chunks */
67 66
68/* Return data level in 1/4-second increments */ 67/* Return data level in 1/4-second increments */
69#define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs)) 68#define DATA_LEVEL(quarter_secs) (pcmbuf_sampr * (quarter_secs))
70 69
71/* Number of bytes played per second: 70/* Number of bytes played per second:
72 (sample rate * 2 channels * 2 bytes/sample) */ 71 (sample rate * 2 channels * 2 bytes/sample) */
73#define BYTERATE (NATIVE_FREQUENCY * 4) 72#define BYTERATE (pcmbuf_sampr * 2 * 2)
74 73
75#if MEMORYSIZE > 2 74#if MEMORYSIZE > 2
76/* Keep watermark high for large memory target - at least (2s) */ 75/* Keep watermark high for large memory target - at least (2s) */
@@ -104,6 +103,7 @@ static size_t pcmbuf_size;
104static struct chunkdesc *pcmbuf_descriptors; 103static struct chunkdesc *pcmbuf_descriptors;
105static unsigned int pcmbuf_desc_count; 104static unsigned int pcmbuf_desc_count;
106static unsigned int position_key = 1; 105static unsigned int position_key = 1;
106static unsigned int pcmbuf_sampr = 0;
107 107
108static size_t chunk_ridx; 108static size_t chunk_ridx;
109static size_t chunk_widx; 109static size_t chunk_widx;
@@ -111,8 +111,7 @@ static size_t chunk_widx;
111static size_t pcmbuf_bytes_waiting; 111static size_t pcmbuf_bytes_waiting;
112static struct chunkdesc *current_desc; 112static struct chunkdesc *current_desc;
113 113
114/* Only written if HAVE_CROSSFADE */ 114static size_t pcmbuf_watermark = 0;
115static size_t pcmbuf_watermark = PCMBUF_WATERMARK;
116 115
117static bool low_latency_mode = false; 116static bool low_latency_mode = false;
118 117
@@ -545,6 +544,8 @@ size_t pcmbuf_init(void *bufend)
545 } 544 }
546 545
547 pcmbuf_finish_crossfade_enable(); 546 pcmbuf_finish_crossfade_enable();
547#else
548 pcmbuf_watermark = PCMBUF_WATERMARK;
548#endif /* HAVE_CROSSFADE */ 549#endif /* HAVE_CROSSFADE */
549 550
550 init_buffer_state(); 551 init_buffer_state();
@@ -1331,3 +1332,13 @@ void pcmbuf_set_low_latency(bool state)
1331{ 1332{
1332 low_latency_mode = state; 1333 low_latency_mode = state;
1333} 1334}
1335
1336void pcmbuf_update_frequency(void)
1337{
1338 pcmbuf_sampr = mixer_get_frequency();
1339}
1340
1341unsigned int pcmbuf_get_frequency(void)
1342{
1343 return pcmbuf_sampr;
1344}
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index 7fa3563e6a..008872be59 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -81,5 +81,7 @@ void pcmbuf_sync_position_update(void);
81/* Misc */ 81/* Misc */
82bool pcmbuf_is_lowdata(void); 82bool pcmbuf_is_lowdata(void);
83void pcmbuf_set_low_latency(bool state); 83void pcmbuf_set_low_latency(bool state);
84void pcmbuf_update_frequency(void);
85unsigned int pcmbuf_get_frequency(void);
84 86
85#endif /* PCMBUF_H */ 87#endif /* PCMBUF_H */
diff --git a/apps/playback.c b/apps/playback.c
index 24c268ffc4..8b498f265e 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -2028,8 +2028,11 @@ static int audio_fill_file_buffer(void)
2028 /* Must reset the buffer before use if trashed or voice only - voice 2028 /* Must reset the buffer before use if trashed or voice only - voice
2029 file size shouldn't have changed so we can go straight from 2029 file size shouldn't have changed so we can go straight from
2030 AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */ 2030 AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */
2031 if (buffer_state != AUDIOBUF_STATE_INITIALIZED) 2031 if (buffer_state != AUDIOBUF_STATE_INITIALIZED ||
2032 !pcmbuf_is_same_size())
2033 {
2032 audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED); 2034 audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED);
2035 }
2033 2036
2034 logf("Starting buffer fill"); 2037 logf("Starting buffer fill");
2035 2038
@@ -2510,6 +2513,11 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2510#ifndef PLATFORM_HAS_VOLUME_CHANGE 2513#ifndef PLATFORM_HAS_VOLUME_CHANGE
2511 sound_set_volume(global_settings.volume); 2514 sound_set_volume(global_settings.volume);
2512#endif 2515#endif
2516#ifdef HAVE_PLAY_FREQ
2517 settings_apply_play_freq(global_settings.play_frequency, true);
2518#endif
2519 pcmbuf_update_frequency();
2520
2513 /* Be sure channel is audible */ 2521 /* Be sure channel is audible */
2514 pcmbuf_fade(false, true); 2522 pcmbuf_fade(false, true);
2515 2523
@@ -3755,6 +3763,7 @@ void INIT_ATTR playback_init(void)
3755 mutex_init(&id3_mutex); 3763 mutex_init(&id3_mutex);
3756 track_list_init(); 3764 track_list_init();
3757 buffering_init(); 3765 buffering_init();
3766 pcmbuf_update_frequency();
3758 add_event(PLAYBACK_EVENT_VOICE_PLAYING, false, playback_voice_event); 3767 add_event(PLAYBACK_EVENT_VOICE_PLAYING, false, playback_voice_event);
3759#ifdef HAVE_CROSSFADE 3768#ifdef HAVE_CROSSFADE
3760 /* Set crossfade setting for next buffer init which should be about... */ 3769 /* Set crossfade setting for next buffer init which should be about... */
diff --git a/apps/plugin.c b/apps/plugin.c
index 24443b58d9..a5cdfc3d8a 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -798,6 +798,8 @@ static const struct plugin_api rockbox_api = {
798 /* new stuff at the end, sort into place next time 798 /* new stuff at the end, sort into place next time
799 the API gets incompatible */ 799 the API gets incompatible */
800 800
801 mixer_set_frequency,
802 mixer_get_frequency,
801}; 803};
802 804
803int plugin_load(const char* plugin, const void* parameter) 805int plugin_load(const char* plugin, const void* parameter)
diff --git a/apps/plugin.h b/apps/plugin.h
index 936f977fdc..f926b3428d 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -155,7 +155,7 @@ void* plugin_get_buffer(size_t *buffer_size);
155#define PLUGIN_MAGIC 0x526F634B /* RocK */ 155#define PLUGIN_MAGIC 0x526F634B /* RocK */
156 156
157/* increase this every time the api struct changes */ 157/* increase this every time the api struct changes */
158#define PLUGIN_API_VERSION 223 158#define PLUGIN_API_VERSION 224
159 159
160/* update this to latest version if a change to the api struct breaks 160/* update this to latest version if a change to the api struct breaks
161 backwards compatibility (and please take the opportunity to sort in any 161 backwards compatibility (and please take the opportunity to sort in any
@@ -970,6 +970,8 @@ struct plugin_api {
970 /* new stuff at the end, sort into place next time 970 /* new stuff at the end, sort into place next time
971 the API gets incompatible */ 971 the API gets incompatible */
972 972
973 void (*mixer_set_frequency)(unsigned int samplerate);
974 unsigned int (*mixer_get_frequency)(void);
973}; 975};
974 976
975/* plugin header */ 977/* plugin header */
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index c512a9e02f..00bf9606d4 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -37,6 +37,8 @@ resistor.c
37remote_control.c 37remote_control.c
38#endif 38#endif
39 39
40test_codec.c
41test_sampr.c
40 42
41 43
42#ifdef HAVE_BACKLIGHT 44#ifdef HAVE_BACKLIGHT
diff --git a/apps/plugins/mpegplayer/audio_thread.c b/apps/plugins/mpegplayer/audio_thread.c
index 1c167ea2a2..764ad111f2 100644
--- a/apps/plugins/mpegplayer/audio_thread.c
+++ b/apps/plugins/mpegplayer/audio_thread.c
@@ -481,6 +481,7 @@ static void audio_thread(void)
481 init_mad(); 481 init_mad();
482 482
483 td.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO); 483 td.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
484 rb->dsp_configure(td.dsp, DSP_SET_OUT_FREQUENCY, CLOCK_RATE);
484#ifdef HAVE_PITCHCONTROL 485#ifdef HAVE_PITCHCONTROL
485 rb->sound_set_pitch(PITCH_SPEED_100); 486 rb->sound_set_pitch(PITCH_SPEED_100);
486 rb->dsp_set_timestretch(PITCH_SPEED_100); 487 rb->dsp_set_timestretch(PITCH_SPEED_100);
diff --git a/apps/plugins/mpegplayer/mpegplayer.h b/apps/plugins/mpegplayer/mpegplayer.h
index 32cc7b25be..4ddf0ca7b1 100644
--- a/apps/plugins/mpegplayer/mpegplayer.h
+++ b/apps/plugins/mpegplayer/mpegplayer.h
@@ -44,7 +44,7 @@
44#define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE) 44#define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE)
45 45
46/** PCM buffer **/ 46/** PCM buffer **/
47#define CLOCK_RATE NATIVE_FREQUENCY /* Our clock rate in ticks/second (samplerate) */ 47#define CLOCK_RATE 44100 /* Our clock rate in ticks/second (samplerate) */
48 48
49/* Define this as "1" to have a test tone instead of silence clip */ 49/* Define this as "1" to have a test tone instead of silence clip */
50#define SILENCE_TEST_TONE 0 50#define SILENCE_TEST_TONE 0
diff --git a/apps/plugins/mpegplayer/pcm_output.c b/apps/plugins/mpegplayer/pcm_output.c
index 3af8e91adc..82e3584277 100644
--- a/apps/plugins/mpegplayer/pcm_output.c
+++ b/apps/plugins/mpegplayer/pcm_output.c
@@ -51,6 +51,8 @@ static uint32_t volatile clock_time IBSS_ATTR; /* Timestamp adjusted */
51static int pcm_skipped = 0; 51static int pcm_skipped = 0;
52static int pcm_underruns = 0; 52static int pcm_underruns = 0;
53 53
54static unsigned int old_sampr = 0;
55
54/* Small silence clip. ~5.80ms @ 44.1kHz */ 56/* Small silence clip. ~5.80ms @ 44.1kHz */
55static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 }; 57static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 };
56 58
@@ -380,9 +382,13 @@ bool pcm_output_init(void)
380 } 382 }
381#endif 383#endif
382 384
385 old_sampr = rb->mixer_get_frequency();
386 rb->mixer_set_frequency(CLOCK_RATE);
383 return true; 387 return true;
384} 388}
385 389
386void pcm_output_exit(void) 390void pcm_output_exit(void)
387{ 391{
392 if (old_sampr != 0)
393 rb->mixer_set_frequency(old_sampr);
388} 394}
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index a4ec6a8789..4d807493d3 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -1200,13 +1200,14 @@ static long anim_peaks_vertical(void)
1200/** Waveform View **/ 1200/** Waveform View **/
1201 1201
1202#ifdef OSCILLOSCOPE_GRAPHMODE 1202#ifdef OSCILLOSCOPE_GRAPHMODE
1203static int16_t waveform_buffer[2*ALIGN_UP(NATIVE_FREQUENCY, 2048)+2*2048] 1203static int16_t waveform_buffer[2*ALIGN_UP(PLAY_SAMPR_MAX, 2048)+2*2048]
1204 MEM_ALIGN_ATTR; 1204 MEM_ALIGN_ATTR;
1205static size_t waveform_buffer_threshold = 0; 1205static size_t waveform_buffer_threshold = 0;
1206static size_t volatile waveform_buffer_have = 0; 1206static size_t volatile waveform_buffer_have = 0;
1207static size_t waveform_buffer_break = 0; 1207static size_t waveform_buffer_break = 0;
1208static unsigned long mixer_sampr = PLAY_SAMPR_DEFAULT;
1208#define PCM_SAMPLESIZE (2*sizeof(int16_t)) 1209#define PCM_SAMPLESIZE (2*sizeof(int16_t))
1209#define PCM_BYTERATE (NATIVE_FREQUENCY*PCM_SAMPLESIZE) 1210#define PCM_BYTERATE(sampr) ((sampr)*PCM_SAMPLESIZE)
1210 1211
1211#define WAVEFORM_SCALE_PCM(full_scale, sample) \ 1212#define WAVEFORM_SCALE_PCM(full_scale, sample) \
1212 ((((full_scale) * (sample)) + (1 << 14)) >> 15) 1213 ((((full_scale) * (sample)) + (1 << 14)) >> 15)
@@ -1390,7 +1391,7 @@ static long anim_waveform_horizontal(void)
1390 return cur_tick + HZ/5; 1391 return cur_tick + HZ/5;
1391 } 1392 }
1392 1393
1393 int count = (NATIVE_FREQUENCY*osc_delay + 100*HZ - 1) / (100*HZ); 1394 int count = (mixer_sampr*osc_delay + 100*HZ - 1) / (100*HZ);
1394 1395
1395 waveform_buffer_set_threshold(count*PCM_SAMPLESIZE); 1396 waveform_buffer_set_threshold(count*PCM_SAMPLESIZE);
1396 1397
@@ -1516,7 +1517,8 @@ static long anim_waveform_horizontal(void)
1516 osd_lcd_update(); 1517 osd_lcd_update();
1517 1518
1518 long delay = get_next_delay(); 1519 long delay = get_next_delay();
1519 return cur_tick + delay - waveform_buffer_have * HZ / PCM_BYTERATE; 1520 return cur_tick + delay - waveform_buffer_have * HZ /
1521 PCM_BYTERATE(mixer_sampr);
1520} 1522}
1521 1523
1522static void anim_waveform_plot_filled_v(int y, int y_prev, 1524static void anim_waveform_plot_filled_v(int y, int y_prev,
@@ -1583,7 +1585,7 @@ static long anim_waveform_vertical(void)
1583 return cur_tick + HZ/5; 1585 return cur_tick + HZ/5;
1584 } 1586 }
1585 1587
1586 int count = (NATIVE_FREQUENCY*osc_delay + 100*HZ - 1) / (100*HZ); 1588 int count = (mixer_sampr*osc_delay + 100*HZ - 1) / (100*HZ);
1587 1589
1588 waveform_buffer_set_threshold(count*PCM_SAMPLESIZE); 1590 waveform_buffer_set_threshold(count*PCM_SAMPLESIZE);
1589 1591
@@ -1709,7 +1711,8 @@ static long anim_waveform_vertical(void)
1709 osd_lcd_update(); 1711 osd_lcd_update();
1710 1712
1711 long delay = get_next_delay(); 1713 long delay = get_next_delay();
1712 return cur_tick + delay - waveform_buffer_have * HZ / PCM_BYTERATE; 1714 return cur_tick + delay - waveform_buffer_have * HZ
1715 / PCM_BYTERATE(mixer_sampr);
1713} 1716}
1714 1717
1715static void anim_waveform_exit(void) 1718static void anim_waveform_exit(void)
@@ -1872,6 +1875,10 @@ static void osc_setup(void)
1872 osd_lcd_update(); 1875 osd_lcd_update();
1873#endif 1876#endif
1874 1877
1878#ifdef OSCILLOSCOPE_GRAPHMODE
1879 mixer_sampr = rb->mixer_get_frequency();
1880#endif
1881
1875 /* Turn off backlight timeout */ 1882 /* Turn off backlight timeout */
1876 backlight_ignore_timeout(); 1883 backlight_ignore_timeout();
1877 graphmode_setup(); 1884 graphmode_setup();
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c
index 7523d9e9aa..0b409f8e35 100644
--- a/apps/plugins/test_codec.c
+++ b/apps/plugins/test_codec.c
@@ -502,7 +502,12 @@ static void configure(int setting, intptr_t value)
502 { 502 {
503 case DSP_SET_FREQUENCY: 503 case DSP_SET_FREQUENCY:
504 DEBUGF("samplerate=%d\n",(int)value); 504 DEBUGF("samplerate=%d\n",(int)value);
505 wavinfo.samplerate = use_dsp ? NATIVE_FREQUENCY : (int)value; 505 if (use_dsp) {
506 wavinfo.samplerate = rb->dsp_configure(
507 ci.dsp, DSP_GET_OUT_FREQUENCY, 0);
508 } else {
509 wavinfo.samplerate = (int)value;
510 }
506 break; 511 break;
507 512
508 case DSP_SET_SAMPLE_DEPTH: 513 case DSP_SET_SAMPLE_DEPTH:
diff --git a/apps/rbcodecconfig.h b/apps/rbcodecconfig.h
index ff9fc41342..cc51595cc4 100644
--- a/apps/rbcodecconfig.h
+++ b/apps/rbcodecconfig.h
@@ -71,4 +71,8 @@ static inline void dsp_process_end(struct dsp_loop_context *ctx)
71 71
72#endif 72#endif
73 73
74#define DSP_OUT_MIN_HZ PLAY_SAMPR_HW_MIN
75#define DSP_OUT_MAX_HZ PLAY_SAMPR_MAX
76#define DSP_OUT_DEFAULT_HZ PLAY_SAMPR_DEFAULT
77
74#endif 78#endif
diff --git a/apps/settings.c b/apps/settings.c
index adc53cd14b..cf51b0793c 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -85,6 +85,11 @@ struct system_status global_status;
85#ifdef HAVE_RECORDING 85#ifdef HAVE_RECORDING
86#include "enc_config.h" 86#include "enc_config.h"
87#endif 87#endif
88#include "pcm_sampr.h"
89#ifdef HAVE_PLAY_FREQ
90#include "pcm_mixer.h"
91#include "dsp_core.h"
92#endif
88#endif /* CONFIG_CODEC == SWCODEC */ 93#endif /* CONFIG_CODEC == SWCODEC */
89 94
90#define NVRAM_BLOCK_SIZE 44 95#define NVRAM_BLOCK_SIZE 44
@@ -720,6 +725,36 @@ void settings_apply_pm_range(void)
720} 725}
721#endif /* HAVE_LCD_BITMAP */ 726#endif /* HAVE_LCD_BITMAP */
722 727
728#ifdef HAVE_PLAY_FREQ
729void settings_apply_play_freq(int value, bool playback)
730{
731 static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48 };
732 static int prev_setting = 0;
733
734 if ((unsigned)value >= ARRAYLEN(play_sampr))
735 value = 0;
736
737 bool changed = value != prev_setting;
738 prev_setting = value;
739
740 long offset = 0;
741 bool playing = changed && !playback &&
742 audio_status() == AUDIO_STATUS_PLAY;
743
744 if (playing)
745 offset = audio_current_track()->offset;
746
747 if (changed && !playback)
748 audio_hard_stop();
749
750 /* Other sub-areas of playback pick it up from the mixer */
751 mixer_set_frequency(play_sampr[value]);
752
753 if (playing)
754 audio_play(offset);
755}
756#endif /* HAVE_PLAY_FREQ */
757
723void sound_settings_apply(void) 758void sound_settings_apply(void)
724{ 759{
725#ifdef AUDIOHW_HAVE_BASS 760#ifdef AUDIOHW_HAVE_BASS
@@ -976,6 +1011,9 @@ void settings_apply(bool read_disk)
976 set_codepage(global_settings.default_codepage); 1011 set_codepage(global_settings.default_codepage);
977 CHART("<set_codepage"); 1012 CHART("<set_codepage");
978 1013
1014#ifdef HAVE_PLAY_FREQ
1015 settings_apply_play_freq(global_settings.play_frequency, false);
1016#endif
979#if CONFIG_CODEC == SWCODEC 1017#if CONFIG_CODEC == SWCODEC
980#ifdef HAVE_CROSSFADE 1018#ifdef HAVE_CROSSFADE
981 audio_set_crossfade(global_settings.crossfade); 1019 audio_set_crossfade(global_settings.crossfade);
diff --git a/apps/settings.h b/apps/settings.h
index 1aec931798..087ff0cb45 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -223,6 +223,9 @@ void settings_apply_skins(void);
223 223
224void settings_apply(bool read_disk); 224void settings_apply(bool read_disk);
225void settings_apply_pm_range(void); 225void settings_apply_pm_range(void);
226#ifdef HAVE_PLAY_FREQ
227void settings_apply_play_freq(int value, bool playback);
228#endif
226void settings_display(void); 229void settings_display(void);
227 230
228enum optiontype { INT, BOOL }; 231enum optiontype { INT, BOOL };
@@ -821,6 +824,10 @@ struct user_settings
821#ifdef HAVE_QUICKSCREEN 824#ifdef HAVE_QUICKSCREEN
822 bool shortcuts_replaces_qs; 825 bool shortcuts_replaces_qs;
823#endif 826#endif
827
828#ifdef HAVE_PLAY_FREQ
829 int play_frequency; /* core audio output frequency selection */
830#endif
824}; 831};
825 832
826/** global variables **/ 833/** global variables **/
diff --git a/apps/settings_list.c b/apps/settings_list.c
index c1b40a64b6..980c74f0a7 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -807,6 +807,11 @@ const struct settings_list settings[] = {
807 ,ID2P(LANG_REPEAT_AB) 807 ,ID2P(LANG_REPEAT_AB)
808#endif 808#endif
809 ), /* CHOICE_SETTING( repeat_mode ) */ 809 ), /* CHOICE_SETTING( repeat_mode ) */
810#ifdef HAVE_PLAY_FREQ
811 STRINGCHOICE_SETTING(0, play_frequency, LANG_PLAYBACK_FREQUENCY, 0,
812 "playback frequency", "44.1 kHz,48 kHz", NULL, 2,
813 TALK_ID_DECIMAL(441, 1, UNIT_KHZ), TALK_ID(48, UNIT_KHZ)),
814#endif /* HAVE_PLAY_FREQ */
810 /* LCD */ 815 /* LCD */
811#ifdef HAVE_LCD_CONTRAST 816#ifdef HAVE_LCD_CONTRAST
812 /* its easier to leave this one un-macro()ed for the time being */ 817 /* its easier to leave this one un-macro()ed for the time being */
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 46471c0d9b..7788f659b0 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -18,7 +18,7 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#include <sys/types.h> 21#include "config.h"
22#include "system.h" 22#include "system.h"
23#include "core_alloc.h" 23#include "core_alloc.h"
24#include "thread.h" 24#include "thread.h"
@@ -30,8 +30,7 @@
30#include "pcm_mixer.h" 30#include "pcm_mixer.h"
31#include "codecs/libspeex/speex/speex.h" 31#include "codecs/libspeex/speex/speex.h"
32 32
33/* Default number of native-frequency PCM frames to queue - adjust as 33/* Default number of PCM frames to queue - adjust as necessary per-target */
34 necessary per-target */
35#define VOICE_FRAMES 4 34#define VOICE_FRAMES 4
36 35
37/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log 36/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log
@@ -84,8 +83,8 @@ static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR;
84static int quiet_counter SHAREDDATA_ATTR = 0; 83static int quiet_counter SHAREDDATA_ATTR = 0;
85static bool voice_playing = false; 84static bool voice_playing = false;
86 85
87#define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_COUNT + \ 86#define VOICE_PCM_FRAME_COUNT ((PLAY_SAMPR_MAX*VOICE_FRAME_COUNT + \
88 VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE) 87 VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE)
89#define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*2*sizeof (int16_t)) 88#define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*2*sizeof (int16_t))
90 89
91/* Voice processing states */ 90/* Voice processing states */
@@ -356,11 +355,13 @@ static enum voice_state voice_message(struct voice_thread_data *td)
356 { 355 {
357 /* Stop any clip still playing */ 356 /* Stop any clip still playing */
358 voice_stop_playback(); 357 voice_stop_playback();
358 dsp_configure(td->dsp, DSP_FLUSH, 0);
359 } 359 }
360 360
361 if (quiet_counter <= 0) 361 if (quiet_counter <= 0)
362 { 362 {
363 voice_playing = true; 363 voice_playing = true;
364 dsp_configure(td->dsp, DSP_SET_OUT_FREQUENCY, mixer_get_frequency());
364 send_event(PLAYBACK_EVENT_VOICE_PLAYING, &voice_playing); 365 send_event(PLAYBACK_EVENT_VOICE_PLAYING, &voice_playing);
365 } 366 }
366 367
diff --git a/firmware/export/config_caps.h b/firmware/export/config_caps.h
index fcb13debfc..bc0a42bedf 100644
--- a/firmware/export/config_caps.h
+++ b/firmware/export/config_caps.h
@@ -116,3 +116,37 @@
116#endif 116#endif
117 117
118#endif /* HAVE_RECORDING */ 118#endif /* HAVE_RECORDING */
119
120/* Samplerate config */
121#define PCM_SAMPR_CONFIG_ONLY /* no C code */
122#include "pcm_sampr.h"
123#undef PCM_SAMPR_CONFIG_ONLY
124
125#define PLAY_SAMPR_CAPS (HW_SAMPR_CAPS & (SAMPR_CAP_44 | SAMPR_CAP_48))
126/**
127 * PLAY_SAMPR_MIN: The minimum allowable samplerate for global playback.
128 * Music won't play at a lower rate.
129 * PLAY_SAMPR_MAX: The maximum allowable samplerate for global playback.
130 * Music won't play at a faster rate.
131 * PLAY_SAMPR_DEFAULT: The default samplerate, unless configured otherwise.
132 * PLAY_SAMPR_HW_MIN: The minimum allowable rate for some subsystems such
133 * as the DSP core. DSP never exceeds *MAX to lessen
134 * buffer allocation demands and overhead.
135 */
136#if PLAY_SAMPR_CAPS & (PLAY_SAMPR_CAPS - 1)
137#define HAVE_PLAY_FREQ
138# define PLAY_SAMPR_MIN SAMPR_44
139# define PLAY_SAMPR_MAX SAMPR_48
140# define PLAY_SAMPR_DEFAULT SAMPR_44
141# define PLAY_SAMPR_HW_MIN HW_SAMPR_MIN
142#elif PLAY_SAMPR_CAPS & SAMPR_CAP_44
143# define PLAY_SAMPR_MIN SAMPR_44
144# define PLAY_SAMPR_MAX SAMPR_44
145# define PLAY_SAMPR_DEFAULT SAMPR_44
146# define PLAY_SAMPR_HW_MIN HW_SAMPR_MIN
147#elif PLAY_SAMPR_CAPS & SAMPR_CAP_48
148# define PLAY_SAMPR_MIN SAMPR_48
149# define PLAY_SAMPR_MAX SAMPR_48
150# define PLAY_SAMPR_DEFAULT SAMPR_48
151# define PLAY_SAMPR_HW_MIN HW_SAMPR_MIN
152#endif
diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h
index fdd46237a6..23c0bd4a0b 100644
--- a/firmware/export/pcm.h
+++ b/firmware/export/pcm.h
@@ -53,7 +53,10 @@ unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate);
53#endif 53#endif
54#endif /* CONFIG_SAMPR_TYPES */ 54#endif /* CONFIG_SAMPR_TYPES */
55 55
56/* set next frequency to be used */
56void pcm_set_frequency(unsigned int samplerate); 57void pcm_set_frequency(unsigned int samplerate);
58/* return last-set frequency */
59unsigned int pcm_get_frequency(void);
57/* apply settings to hardware immediately */ 60/* apply settings to hardware immediately */
58void pcm_apply_settings(void); 61void pcm_apply_settings(void);
59 62
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h
index d424083002..f7f869eaaf 100644
--- a/firmware/export/pcm_mixer.h
+++ b/firmware/export/pcm_mixer.h
@@ -127,4 +127,10 @@ void mixer_channel_set_buffer_hook(enum pcm_mixer_channel channel,
127/* Stop ALL channels and PCM and reset state */ 127/* Stop ALL channels and PCM and reset state */
128void mixer_reset(void); 128void mixer_reset(void);
129 129
130/* Set output samplerate */
131void mixer_set_frequency(unsigned int samplerate);
132
133/* Get output samplerate */
134unsigned int mixer_get_frequency(void);
135
130#endif /* PCM_MIXER_H */ 136#endif /* PCM_MIXER_H */
diff --git a/firmware/export/pcm_sampr.h b/firmware/export/pcm_sampr.h
index 01a8ed428e..dcb1bdd80f 100644
--- a/firmware/export/pcm_sampr.h
+++ b/firmware/export/pcm_sampr.h
@@ -20,7 +20,12 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#ifndef PCM_SAMPR_H 22#ifndef PCM_SAMPR_H
23
24/* File might be included for CPP config macros only. Allow it to be included
25 * again for full C declarations. */
26#ifndef PCM_SAMPR_CONFIG_ONLY
23#define PCM_SAMPR_H 27#define PCM_SAMPR_H
28#endif
24 29
25#ifndef HW_SAMPR_CAPS 30#ifndef HW_SAMPR_CAPS
26#define HW_SAMPR_CAPS SAMPR_CAP_44 /* if not defined, default to 44100 */ 31#define HW_SAMPR_CAPS SAMPR_CAP_44 /* if not defined, default to 44100 */
@@ -75,11 +80,14 @@
75 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ 80 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
76 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) 81 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
77 82
83#ifndef PCM_SAMPR_CONFIG_ONLY
78/* Master list of all "standard" rates supported. */ 84/* Master list of all "standard" rates supported. */
79extern const unsigned long audio_master_sampr_list[SAMPR_NUM_FREQ]; 85extern const unsigned long audio_master_sampr_list[SAMPR_NUM_FREQ];
86#endif /* PCM_SAMPR_CONFIG_ONLY */
80 87
81/** Hardware sample rates **/ 88/** Hardware sample rates **/
82 89
90#ifndef PCM_SAMPR_CONFIG_ONLY
83/* Enumeration of supported frequencies where 0 is the highest rate 91/* Enumeration of supported frequencies where 0 is the highest rate
84 supported and REC_NUM_FREQUENCIES is the number available */ 92 supported and REC_NUM_FREQUENCIES is the number available */
85enum hw_freq_indexes 93enum hw_freq_indexes
@@ -183,14 +191,49 @@ enum hw_freq_indexes
183#define HW_HAVE_8_(...) 191#define HW_HAVE_8_(...)
184#endif 192#endif
185 HW_NUM_FREQ, 193 HW_NUM_FREQ,
186 HW_FREQ_DEFAULT = HW_FREQ_44,
187 HW_SAMPR_DEFAULT = SAMPR_44,
188}; /* enum hw_freq_indexes */ 194}; /* enum hw_freq_indexes */
189 195
190/* list of hardware sample rates */ 196/* list of hardware sample rates */
191extern const unsigned long hw_freq_sampr[HW_NUM_FREQ]; 197extern const unsigned long hw_freq_sampr[HW_NUM_FREQ];
198#endif /* PCM_SAMPR_CONFIG_ONLY */
199
200#define HW_FREQ_DEFAULT HW_FREQ_44
201#define HW_SAMPR_DEFAULT SAMPR_44
202
203
204#if HW_SAMPR_CAPS & SAMPR_CAP_96
205# define HW_SAMPR_MAX SAMPR_96
206#elif HW_SAMPR_CAPS & SAMPR_CAP_88
207# define HW_SAMPR_MAX SAMPR_88
208#elif HW_SAMPR_CAPS & SAMPR_CAP_64
209# define HW_SAMPR_MAX SAMPR_64
210#elif HW_SAMPR_CAPS & SAMPR_CAP_48
211# define HW_SAMPR_MAX SAMPR_48
212#else
213# define HW_SAMPR_MAX SAMPR_44
214#endif
215
216#if HW_SAMPR_CAPS & SAMPR_CAP_8
217# define HW_SAMPR_MIN SAMPR_8
218#elif HW_SAMPR_CAPS & SAMPR_CAP_11
219# define HW_SAMPR_MIN SAMPR_11
220#elif HW_SAMPR_CAPS & SAMPR_CAP_12
221# define HW_SAMPR_MIN SAMPR_12
222#elif HW_SAMPR_CAPS & SAMPR_CAP_16
223# define HW_SAMPR_MIN SAMPR_16
224#elif HW_SAMPR_CAPS & SAMPR_CAP_22
225# define HW_SAMPR_MIN SAMPR_22
226#elif HW_SAMPR_CAPS & SAMPR_CAP_24
227# define HW_SAMPR_MIN SAMPR_24
228#elif HW_SAMPR_CAPS & SAMPR_CAP_32
229# define HW_SAMPR_MIN SAMPR_32
230#else
231# define HW_SAMPR_MIN SAMPR_44
232#endif
192 233
193#ifdef HAVE_RECORDING 234#ifdef HAVE_RECORDING
235
236#ifndef PCM_SAMPR_CONFIG_ONLY
194/* Enumeration of supported frequencies where 0 is the highest rate 237/* Enumeration of supported frequencies where 0 is the highest rate
195 supported and REC_NUM_FREQUENCIES is the number available */ 238 supported and REC_NUM_FREQUENCIES is the number available */
196enum rec_freq_indexes 239enum rec_freq_indexes
@@ -296,6 +339,10 @@ enum rec_freq_indexes
296 REC_NUM_FREQ, 339 REC_NUM_FREQ,
297}; /* enum rec_freq_indexes */ 340}; /* enum rec_freq_indexes */
298 341
342/* List of recording supported sample rates (set or subset of master list) */
343extern const unsigned long rec_freq_sampr[REC_NUM_FREQ];
344#endif /* PCM_SAMPR_CONFIG_ONLY */
345
299/* Default to 44.1kHz if not otherwise specified */ 346/* Default to 44.1kHz if not otherwise specified */
300#ifndef REC_FREQ_DEFAULT 347#ifndef REC_FREQ_DEFAULT
301#define REC_FREQ_DEFAULT REC_FREQ_44 348#define REC_FREQ_DEFAULT REC_FREQ_44
@@ -314,8 +361,7 @@ enum rec_freq_indexes
314 REC_HAVE_16_(",16") REC_HAVE_12_(",12") \ 361 REC_HAVE_16_(",16") REC_HAVE_12_(",12") \
315 REC_HAVE_11_(",11") REC_HAVE_8_(",8")[1] 362 REC_HAVE_11_(",11") REC_HAVE_8_(",8")[1]
316 363
317/* List of recording supported sample rates (set or subset of master list) */ 364
318extern const unsigned long rec_freq_sampr[REC_NUM_FREQ];
319#endif /* HAVE_RECORDING */ 365#endif /* HAVE_RECORDING */
320 366
321#ifdef CONFIG_SAMPR_TYPES 367#ifdef CONFIG_SAMPR_TYPES
@@ -326,8 +372,10 @@ extern const unsigned long rec_freq_sampr[REC_NUM_FREQ];
326#define SAMPR_TYPE_REC (0x01 << 24) 372#define SAMPR_TYPE_REC (0x01 << 24)
327#endif 373#endif
328 374
375#ifndef PCM_SAMPR_CONFIG_ONLY
329unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate, 376unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate,
330 unsigned int type); 377 unsigned int type);
378#endif
331 379
332#else /* ndef CONFIG_SAMPR_TYPES */ 380#else /* ndef CONFIG_SAMPR_TYPES */
333 381
diff --git a/firmware/pcm.c b/firmware/pcm.c
index e095ab2cea..60ccdbd2fc 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -415,6 +415,12 @@ void pcm_set_frequency(unsigned int samplerate)
415 pcm_fsel = index; 415 pcm_fsel = index;
416} 416}
417 417
418/* return last-set frequency */
419unsigned int pcm_get_frequency(void)
420{
421 return pcm_sampr;
422}
423
418/* apply pcm settings to the hardware */ 424/* apply pcm settings to the hardware */
419void pcm_apply_settings(void) 425void pcm_apply_settings(void)
420{ 426{
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c
index 34852e97e9..ceba31962e 100644
--- a/firmware/pcm_mixer.c
+++ b/firmware/pcm_mixer.c
@@ -25,7 +25,6 @@
25#include "pcm.h" 25#include "pcm.h"
26#include "pcm-internal.h" 26#include "pcm-internal.h"
27#include "pcm_mixer.h" 27#include "pcm_mixer.h"
28#include "dsp_core.h" /* For NATIVE_FREQUENCY */
29 28
30/* Channels use standard-style PCM callback interface but a latency of one 29/* Channels use standard-style PCM callback interface but a latency of one
31 frame by double-buffering is introduced in order to facilitate mixing and 30 frame by double-buffering is introduced in order to facilitate mixing and
@@ -33,6 +32,8 @@
33 before the last samples are sent to the codec and so things are done in 32 before the last samples are sent to the codec and so things are done in
34 parallel (as much as possible) with sending-out data. */ 33 parallel (as much as possible) with sending-out data. */
35 34
35static unsigned int mixer_sampr = HW_SAMPR_DEFAULT;
36
36/* Define this to nonzero to add a marker pulse at each frame start */ 37/* Define this to nonzero to add a marker pulse at each frame start */
37#define FRAME_BOUNDARY_MARKERS 0 38#define FRAME_BOUNDARY_MARKERS 0
38 39
@@ -65,7 +66,7 @@ static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR;
65static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR; 66static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR;
66 67
67/* Number of silence frames to play after all data has played */ 68/* Number of silence frames to play after all data has played */
68#define MAX_IDLE_FRAMES (NATIVE_FREQUENCY*3 / MIX_FRAME_SAMPLES) 69#define MAX_IDLE_FRAMES (mixer_sampr*3 / MIX_FRAME_SAMPLES)
69static unsigned int idle_counter = 0; 70static unsigned int idle_counter = 0;
70 71
71/** Mixing routines, CPU optmized **/ 72/** Mixing routines, CPU optmized **/
@@ -256,7 +257,7 @@ static void mixer_start_pcm(void)
256#endif 257#endif
257 258
258 /* Requires a shared global sample rate for all channels */ 259 /* Requires a shared global sample rate for all channels */
259 pcm_set_frequency(NATIVE_FREQUENCY); 260 pcm_set_frequency(mixer_sampr);
260 261
261 /* Prepare initial frames and set up the double buffer */ 262 /* Prepare initial frames and set up the double buffer */
262 mixer_buffer_callback(PCM_DMAST_STARTED); 263 mixer_buffer_callback(PCM_DMAST_STARTED);
@@ -438,3 +439,23 @@ void mixer_reset(void)
438 439
439 idle_counter = 0; 440 idle_counter = 0;
440} 441}
442
443/* Set output samplerate */
444void mixer_set_frequency(unsigned int samplerate)
445{
446 pcm_set_frequency(samplerate);
447 samplerate = pcm_get_frequency();
448
449 if (samplerate == mixer_sampr)
450 return;
451
452 /* All data is now invalid */
453 mixer_reset();
454 mixer_sampr = samplerate;
455}
456
457/* Get output samplerate */
458unsigned int mixer_get_frequency(void)
459{
460 return mixer_sampr;
461}
diff --git a/lib/rbcodec/dsp/compressor.c b/lib/rbcodec/dsp/compressor.c
index fc73f761a7..a222caed7f 100644
--- a/lib/rbcodec/dsp/compressor.c
+++ b/lib/rbcodec/dsp/compressor.c
@@ -28,6 +28,7 @@
28#include "logf.h" 28#include "logf.h"
29#include "dsp_proc_entry.h" 29#include "dsp_proc_entry.h"
30#include "compressor.h" 30#include "compressor.h"
31#include "dsp_misc.h"
31 32
32static struct compressor_settings curr_set; /* Cached settings */ 33static struct compressor_settings curr_set; /* Cached settings */
33 34
@@ -40,7 +41,8 @@ static int32_t release_gain IBSS_ATTR; /* S7.24 format */
40 41
41/** COMPRESSOR UPDATE 42/** COMPRESSOR UPDATE
42 * Called via the menu system to configure the compressor process */ 43 * Called via the menu system to configure the compressor process */
43static bool compressor_update(const struct compressor_settings *settings) 44static bool compressor_update(struct dsp_config *dsp,
45 const struct compressor_settings *settings)
44{ 46{
45 /* make settings values useful */ 47 /* make settings values useful */
46 int threshold = settings->threshold; 48 int threshold = settings->threshold;
@@ -48,9 +50,10 @@ static bool compressor_update(const struct compressor_settings *settings)
48 static const int comp_ratios[] = { 2, 4, 6, 10, 0 }; 50 static const int comp_ratios[] = { 2, 4, 6, 10, 0 };
49 int ratio = comp_ratios[settings->ratio]; 51 int ratio = comp_ratios[settings->ratio];
50 bool soft_knee = settings->knee == 1; 52 bool soft_knee = settings->knee == 1;
51 int release = settings->release_time * NATIVE_FREQUENCY / 1000; 53 int release = settings->release_time *
54 dsp_get_output_frequency(dsp) / 1000;
52 55
53 bool changed = false; 56 bool changed = settings == &curr_set; /* If frequency change */
54 bool active = threshold < 0; 57 bool active = threshold < 0;
55 58
56 if (memcmp(settings, &curr_set, sizeof (curr_set))) 59 if (memcmp(settings, &curr_set, sizeof (curr_set)))
@@ -300,8 +303,8 @@ static inline int32_t get_compression_gain(struct sample_format *format,
300void dsp_set_compressor(const struct compressor_settings *settings) 303void dsp_set_compressor(const struct compressor_settings *settings)
301{ 304{
302 /* enable/disable the compressor depending upon settings */ 305 /* enable/disable the compressor depending upon settings */
303 bool enable = compressor_update(settings);
304 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); 306 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
307 bool enable = compressor_update(dsp, settings);
305 dsp_proc_enable(dsp, DSP_PROC_COMPRESSOR, enable); 308 dsp_proc_enable(dsp, DSP_PROC_COMPRESSOR, enable);
306 dsp_proc_activate(dsp, DSP_PROC_COMPRESSOR, true); 309 dsp_proc_activate(dsp, DSP_PROC_COMPRESSOR, true);
307} 310}
@@ -386,15 +389,20 @@ static intptr_t compressor_configure(struct dsp_proc_entry *this,
386 break; /* Already enabled */ 389 break; /* Already enabled */
387 390
388 this->process = compressor_process; 391 this->process = compressor_process;
392 /* Won't have been getting frequency updates */
393 compressor_update(dsp, &curr_set);
389 /* Fall-through */ 394 /* Fall-through */
390 case DSP_RESET: 395 case DSP_RESET:
391 case DSP_FLUSH: 396 case DSP_FLUSH:
392 release_gain = UNITY; 397 release_gain = UNITY;
393 break; 398 break;
399
400 case DSP_SET_OUT_FREQUENCY:
401 compressor_update(dsp, &curr_set);
402 break;
394 } 403 }
395 404
396 return 0; 405 return 0;
397 (void)dsp;
398} 406}
399 407
400/* Database entry */ 408/* Database entry */
diff --git a/lib/rbcodec/dsp/crossfeed.c b/lib/rbcodec/dsp/crossfeed.c
index 36a98f1f33..fc40c6b4d5 100644
--- a/lib/rbcodec/dsp/crossfeed.c
+++ b/lib/rbcodec/dsp/crossfeed.c
@@ -24,6 +24,7 @@
24#include "fixedpoint.h" 24#include "fixedpoint.h"
25#include "fracmul.h" 25#include "fracmul.h"
26#include "replaygain.h" 26#include "replaygain.h"
27#include "dsp_misc.h"
27#include "dsp_proc_entry.h" 28#include "dsp_proc_entry.h"
28#include "dsp_filter.h" 29#include "dsp_filter.h"
29#include "crossfeed.h" 30#include "crossfeed.h"
@@ -44,32 +45,40 @@ void crossfeed_meier_process(struct dsp_proc_entry *this,
44 * to listen to on headphones with no crossfeed. 45 * to listen to on headphones with no crossfeed.
45 */ 46 */
46 47
48#define DELAY_LEN(fs) ((300*(fs) / 1000000)*2) /* ~300 uS */
49
47/* Crossfeed */ 50/* Crossfeed */
48static struct crossfeed_state 51static struct crossfeed_state
49{ 52{
50 int32_t gain; /* 00h: Direct path gain */
51 int32_t coefs[3]; /* 04h: Coefficients for the shelving filter */
52 union 53 union
53 { 54 {
54 struct /* 10h: Data for meier crossfeed */ 55 struct /* Data for meier crossfeed */
55 { 56 {
56 int32_t vcl; 57 int32_t reserved; /* 00h: Reserved: overlaps gain */
57 int32_t vcr; 58 int32_t vcl; /* 04h: Left filter output */
58 int32_t vdiff; 59 int32_t vcr; /* 08h: Right filter output */
59 int32_t coef1; 60 int32_t vdiff; /* 0ch: L-R difference signal */
60 int32_t coef2; 61 int32_t coef1; /* 10h: Left/right filter coef */
62 int32_t coef2; /* 14h: Crossfeed filter coef */
61 }; 63 };
62 struct /* 10h: Data for custom crossfeed */ 64 struct /* Data for custom crossfeed */
63 { 65 {
66 int32_t gain; /* 00h: Direct path gain */
67 int32_t coefs[3]; /* 04h: Filter coefficients: b0, b1, a1 */
64 int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */ 68 int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */
65 int32_t delay[13*2];/* 20h: Delay line buffer (L + R interleaved) */ 69 int32_t *index; /* 20h: Current pointer into the delay line */
70 int32_t *index_max; /* 24h: Current max pointer of delay line */
71 /* 28h: Delay line buffer (L + R interleaved) */
72 int32_t delay[DELAY_LEN(DSP_OUT_MAX_HZ)]; /* Target-dependent size */
66 }; 73 };
67 }; 74 };
68 int32_t *index; /* 88h: Current pointer into the delay line */
69 /* 8ch */
70} crossfeed_state IBSS_ATTR; 75} crossfeed_state IBSS_ATTR;
71 76
72static int crossfeed_type = CROSSFEED_TYPE_NONE; 77static int crossfeed_type = CROSSFEED_TYPE_NONE;
78/* Cached custom settings */
79static long crossfeed_lf_gain;
80static long crossfeed_hf_gain;
81static long crossfeed_cutoff;
73 82
74/* Discard the sample histories */ 83/* Discard the sample histories */
75static void crossfeed_flush(struct dsp_proc_entry *this) 84static void crossfeed_flush(struct dsp_proc_entry *this)
@@ -82,12 +91,49 @@ static void crossfeed_flush(struct dsp_proc_entry *this)
82 } 91 }
83 else 92 else
84 { 93 {
85 memset(state->history, 0, 94 memset(state->history, 0, sizeof (state->history));
86 sizeof (state->history) + sizeof (state->delay)); 95 memset(state->delay, 0, sizeof (state->delay));
87 state->index = state->delay; 96 state->index = state->delay;
88 } 97 }
89} 98}
90 99
100static void crossfeed_meier_update_filter(struct crossfeed_state *state,
101 unsigned int fout)
102{
103 /* 1 / (F.Rforward.C) */
104 state->coef1 = fp_div(2128, fout, 31);
105 /* 1 / (F.Rcross.C) */
106 state->coef2 = fp_div(1000, fout, 31);
107}
108
109static void crossfeed_custom_update_filter(struct crossfeed_state *state,
110 unsigned int fout)
111{
112 long lf_gain = crossfeed_lf_gain;
113 long hf_gain = crossfeed_hf_gain;
114 long cutoff = crossfeed_cutoff;
115 int32_t *c = state->coefs;
116
117 long scaler = get_replaygain_int(lf_gain * 10) << 7;
118
119 cutoff = fp_div(cutoff, fout, 32);
120 hf_gain -= lf_gain;
121 /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB
122 * point instead of shelf midpoint. This is for compatibility with the old
123 * crossfeed shelf filter and should be removed if crossfeed settings are
124 * ever made incompatible for any other good reason.
125 */
126 cutoff = fp_div(cutoff, get_replaygain_int(hf_gain*5), 24);
127
128 filter_shelf_coefs(cutoff, hf_gain, false, c);
129 /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains
130 * over 1 and can do this safely
131 */
132 c[0] = FRACMUL_SHL(c[0], scaler, 4);
133 c[1] = FRACMUL_SHL(c[1], scaler, 4);
134 c[2] <<= 4;
135}
136
91 137
92/** DSP interface **/ 138/** DSP interface **/
93 139
@@ -114,24 +160,13 @@ void dsp_set_crossfeed_direct_gain(int gain)
114/* Both gains should be below 0 dB */ 160/* Both gains should be below 0 dB */
115void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff) 161void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff)
116{ 162{
117 int32_t *c = crossfeed_state.coefs; 163 crossfeed_lf_gain = lf_gain;
118 long scaler = get_replaygain_int(lf_gain * 10) << 7; 164 crossfeed_hf_gain = hf_gain;
165 crossfeed_cutoff = cutoff;
119 166
120 cutoff = 0xffffffff / NATIVE_FREQUENCY * cutoff; 167 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
121 hf_gain -= lf_gain; 168 crossfeed_custom_update_filter(&crossfeed_state,
122 /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB 169 dsp_get_output_frequency(dsp));
123 * point instead of shelf midpoint. This is for compatibility with the old
124 * crossfeed shelf filter and should be removed if crossfeed settings are
125 * ever made incompatible for any other good reason.
126 */
127 cutoff = fp_div(cutoff, get_replaygain_int(hf_gain*5), 24);
128 filter_shelf_coefs(cutoff, hf_gain, false, c);
129 /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains
130 * over 1 and can do this safely
131 */
132 c[0] = FRACMUL_SHL(c[0], scaler, 4);
133 c[1] = FRACMUL_SHL(c[1], scaler, 4);
134 c[2] <<= 4;
135} 170}
136 171
137#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM) 172#if !defined(CPU_COLDFIRE) && !defined(CPU_ARM)
@@ -147,6 +182,7 @@ void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
147 int32_t *coefs = &state->coefs[0]; 182 int32_t *coefs = &state->coefs[0];
148 int32_t gain = state->gain; 183 int32_t gain = state->gain;
149 int32_t *di = state->index; 184 int32_t *di = state->index;
185 int32_t *di_max = state->index_max;
150 186
151 int count = buf->remcount; 187 int count = buf->remcount;
152 188
@@ -176,7 +212,7 @@ void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p)
176 buf->p32[1][i] = FRACMUL(right, gain) + hist_l[1]; 212 buf->p32[1][i] = FRACMUL(right, gain) + hist_l[1];
177 213
178 /* Wrap delay line index if bigger than delay line size */ 214 /* Wrap delay line index if bigger than delay line size */
179 if (di >= delay + 13*2) 215 if (di >= di_max)
180 di = delay; 216 di = delay;
181 } 217 }
182 218
@@ -234,17 +270,21 @@ static void update_process_fn(struct dsp_proc_entry *this,
234 struct dsp_config *dsp) 270 struct dsp_config *dsp)
235{ 271{
236 struct crossfeed_state *state = (struct crossfeed_state *)this->data; 272 struct crossfeed_state *state = (struct crossfeed_state *)this->data;
237 dsp_proc_fn_type fn = crossfeed_process; 273 dsp_proc_fn_type fn;
274
275 unsigned int fout = dsp_get_output_frequency(dsp);
238 276
239 if (crossfeed_type != CROSSFEED_TYPE_CUSTOM) 277 if (crossfeed_type != CROSSFEED_TYPE_CUSTOM)
240 { 278 {
241 /* Set up for Meier */ 279 crossfeed_meier_update_filter(state, fout);
242 /* 1 / (F.Rforward.C) */
243 state->coef1 = (0x7fffffff / NATIVE_FREQUENCY) * 2128;
244 /* 1 / (F.Rcross.C) */
245 state->coef2 = (0x7fffffff / NATIVE_FREQUENCY) * 1000;
246 fn = crossfeed_meier_process; 280 fn = crossfeed_meier_process;
247 } 281 }
282 else
283 {
284 state->index_max = state->delay + DELAY_LEN(fout);
285 crossfeed_custom_update_filter(state, fout);
286 fn = crossfeed_process;
287 }
248 288
249 if (this->process != fn) 289 if (this->process != fn)
250 { 290 {
@@ -292,6 +332,7 @@ static intptr_t crossfeed_configure(struct dsp_proc_entry *this,
292 if (value == 0) 332 if (value == 0)
293 this->data = (intptr_t)&crossfeed_state; 333 this->data = (intptr_t)&crossfeed_state;
294 334
335 case DSP_SET_OUT_FREQUENCY:
295 update_process_fn(this, dsp); 336 update_process_fn(this, dsp);
296 break; 337 break;
297 338
diff --git a/lib/rbcodec/dsp/dsp_arm.S b/lib/rbcodec/dsp/dsp_arm.S
index ed58bed340..16394b8690 100644
--- a/lib/rbcodec/dsp/dsp_arm.S
+++ b/lib/rbcodec/dsp/dsp_arm.S
@@ -196,55 +196,56 @@ crossfeed_process:
196 @ to keep the count on the stack :/ 196 @ to keep the count on the stack :/
197 ldr r1, [r1] @ r1 = buf = *buf_p; 197 ldr r1, [r1] @ r1 = buf = *buf_p;
198 stmfd sp!, { r4-r11, lr } @ stack modified regs 198 stmfd sp!, { r4-r11, lr } @ stack modified regs
199 ldr r12, [r1] @ r12 = buf->remcount 199 ldr r0, [r0] @ r0 = this->data = &crossfeed_state
200 ldr r14, [r0] @ r14 = this->data = &crossfeed_state 200 ldmia r1, { r1-r3 } @ r1 = buf->remcount, r2 = buf->p32[0],
201 ldmib r1, { r2-r3 } @ r2 = buf->p32[0], r3 = buf->p32[1] 201 @ r3 = buf->p32[1]
202 ldmia r14!, { r4-r11 } @ load direct gain and filter data 202 ldmia r0, { r4-r12, r14 } @ r4 = gain, r5-r7 = coeffs,
203 add r0, r14, #13*2*4 @ calculate end of delay 203 @ r8-r11 = history, r12 = index,
204 stmfd sp!, { r0, r12 } @ stack end of delay adr, count and state 204 @ r14 = index_max
205 ldr r0, [r0] @ fetch current delay line address 205 add r0, r0, #0x28 @ r0 = state->delay
206 206 stmfd sp!, { r0-r1, r14 } @ stack state->delay, count, index_max
207 /* Register usage in loop: 207
208 * r0 = &delay[index][0], r1 = accumulator high, r2 = buf->p32[0], 208 /* Register usage in loop:
209 * r0 = acc low/count, r1 = acc high, r2 = buf->p32[0],
209 * r3 = buf->p32[1], r4 = direct gain, r5-r7 = b0, b1, a1 (filter coefs), 210 * r3 = buf->p32[1], r4 = direct gain, r5-r7 = b0, b1, a1 (filter coefs),
210 * r8-r11 = filter history, r12 = temp, r14 = accumulator low 211 * r8 = dr[n-1], r9 = y_r[n-1], r10 = dl[n-1], r11 = y_l[n-1],
212 * r12 = index, r14 = scratch/index_max
211 */ 213 */
212.cfloop: 214.cfloop:
213 smull r14, r1, r6, r8 @ acc = b1*dr[n - 1] 215 smull r0, r1, r6, r8 @ acc = b1*dr[n - 1]
214 smlal r14, r1, r7, r9 @ acc += a1*y_l[n - 1] 216 ldr r8, [r12, #4] @ r8 = dr[n]
215 ldr r8, [r0, #4] @ r8 = dr[n] 217 smlal r0, r1, r7, r9 @ acc += a1*y_r[n - 1]
216 smlal r14, r1, r5, r8 @ acc += b0*dr[n] 218 smlal r0, r1, r5, r8 @ acc += b0*dr[n]
217 mov r9, r1, lsl #1 @ fix format for filter history 219 ldr r14, [r2] @ load left input: x_l[n]
218 ldr r12, [r2] @ load left input 220 mov r9, r1, asl #1 @ fix format for filter history
219 smlal r14, r1, r4, r12 @ acc += gain*x_l[n] 221 smlal r0, r1, r4, r14 @ acc += gain*x_l[n]
220 mov r1, r1, lsl #1 @ fix format 222 mov r1, r1, asl #1 @ fix format
221 str r1, [r2], #4 @ save result 223 str r1, [r2], #4 @ save result
222 224 smull r0, r1, r6, r10 @ acc = b1*dl[n - 1]
223 smull r14, r1, r6, r10 @ acc = b1*dl[n - 1] 225 ldr r10, [r12] @ r10 = dl[n]
224 smlal r14, r1, r7, r11 @ acc += a1*y_r[n - 1] 226 smlal r0, r1, r7, r11 @ acc += a1*y_l[n - 1]
225 ldr r10, [r0] @ r10 = dl[n] 227 smlal r0, r1, r5, r10 @ acc += b0*dl[n]
226 str r12, [r0], #4 @ save left input to delay line 228 str r14, [r12], #4 @ save left input to delay line
227 smlal r14, r1, r5, r10 @ acc += b0*dl[n] 229 ldr r14, [r3] @ load right input: x_r[n]
228 mov r11, r1, lsl #1 @ fix format for filter history 230 mov r11, r1, asl #1 @ fix format for filter history
229 ldr r12, [r3] @ load right input 231 smlal r0, r1, r4, r14 @ acc += gain*x_r[n]
230 smlal r14, r1, r4, r12 @ acc += gain*x_r[n] 232 str r14, [r12], #4 @ save right input to delay line
231 str r12, [r0], #4 @ save right input to delay line 233 ldmib sp, { r0, r14 } @ fetch count and delay end
232 mov r1, r1, lsl #1 @ fix format 234 mov r1, r1, asl #1 @ fix format
233 ldmia sp, { r12, r14 } @ fetch delay line end addr and count from stack
234 str r1, [r3], #4 @ save result 235 str r1, [r3], #4 @ save result
235 236
236 cmp r0, r12 @ need to wrap to start of delay? 237 cmp r12, r14 @ need to wrap to start of delay?
237 subhs r0, r12, #13*2*4 @ wrap back delay line ptr to start 238 ldrhs r12, [sp] @ wrap delay index
238 239
239 subs r14, r14, #1 @ are we finished? 240 subs r0, r0, #1 @ are we finished?
240 strgt r14, [sp, #4] @ nope, save count back to stack 241 strgt r0, [sp, #4] @ save count to stack
241 bgt .cfloop 242 bgt .cfloop
242 243
243 @ save data back to struct 244 @ save data back to struct
244 str r0, [r12] @ save delay line index 245 ldr r0, [sp] @ fetch state->delay
245 sub r12, r12, #13*2*4 + 4*4 @ r12 = data->history 246 sub r0, r0, #0x18 @ save filter history and delay index
246 stmia r12, { r8-r11 } @ save filter history 247 stmia r0, { r8-r12 } @
247 add sp, sp, #8 @ remove temp variables from stack 248 add sp, sp, #12 @ remove temp variables from stack
248 ldmpc regs=r4-r11 249 ldmpc regs=r4-r11
249 .size crossfeed_process, .-crossfeed_process 250 .size crossfeed_process, .-crossfeed_process
250 251
@@ -260,8 +261,7 @@ crossfeed_meier_process:
260 ldr r0, [r0] @ r0 = this->data = &crossfeed_state 261 ldr r0, [r0] @ r0 = this->data = &crossfeed_state
261 stmfd sp!, { r4-r10, lr } @ stack non-volatile context 262 stmfd sp!, { r4-r10, lr } @ stack non-volatile context
262 ldmia r1, { r1-r3 } @ r1 = buf->remcout, r2=p32[0], r3=p32[1] 263 ldmia r1, { r1-r3 } @ r1 = buf->remcout, r2=p32[0], r3=p32[1]
263 add r0, r0, #16 @ r0 = &state->vcl 264 ldmib r0, { r4-r8 } @ r4 = vcl, r5 = vcr, r6 = vdiff
264 ldmia r0, { r4-r8 } @ r4 = vcl, r5 = vcr, r6 = vdiff
265 @ r7 = coef1, r8 = coef2 265 @ r7 = coef1, r8 = coef2
266.cfm_loop: 266.cfm_loop:
267 ldr r12, [r2] @ r12 = lout 267 ldr r12, [r2] @ r12 = lout
@@ -285,7 +285,7 @@ crossfeed_meier_process:
285 sub r5, r5, r12 @ r5 = vcr -= res2 285 sub r5, r5, r12 @ r5 = vcr -= res2
286 bgt .cfm_loop @ more samples? 286 bgt .cfm_loop @ more samples?
287 287
288 stmia r0, { r4-r6 } @ save vcl, vcr, vdiff 288 stmib r0, { r4-r6 } @ save vcl, vcr, vdiff
289 ldmpc regs=r4-r10 @ restore non-volatile context, return 289 ldmpc regs=r4-r10 @ restore non-volatile context, return
290 .size crossfeed_meier_process, .-crossfeed_meier_process 290 .size crossfeed_meier_process, .-crossfeed_meier_process
291 291
diff --git a/lib/rbcodec/dsp/dsp_cf.S b/lib/rbcodec/dsp/dsp_cf.S
index 02db8f61b6..e34075ef9a 100644
--- a/lib/rbcodec/dsp/dsp_cf.S
+++ b/lib/rbcodec/dsp/dsp_cf.S
@@ -81,58 +81,60 @@ crossfeed_process:
81 movem.l %d2-%d7/%a2-%a6, (%sp) | save all regs 81 movem.l %d2-%d7/%a2-%a6, (%sp) | save all regs
82 movem.l 48(%sp), %a1/%a4 | %a1 = this, %a4 = buf_p 82 movem.l 48(%sp), %a1/%a4 | %a1 = this, %a4 = buf_p
83 move.l (%a4), %a4 | %a4 = buf = *buf_p 83 move.l (%a4), %a4 | %a4 = buf = *buf_p
84 movem.l (%a4), %d7/%a4-%a5 | %d7 = buf->remcount, %a4 = buf->p32[0], 84 movem.l (%a4), %d0/%a4-%a5 | %d0 = buf->remcount, %a4 = buf->p32[0],
85 | %a5 = buf->p32[1] 85 | %a5 = buf->p32[1]
86 move.l (%a1), %a1 | %a1 = &crossfeed_state 86 move.l (%a1), %a6 | %d7 = state = &crossfeed_state
87 move.l (%a1)+, %d6 | %d6 = direct gain 87 movem.l (%a6), %d1-%d6/%a0-%a3 | %d1 = gain, %d2-%d4 = coefs,
88 movem.l 12(%a1), %d0-%d3 | fetch filter history samples 88 | %d5..%d6 = history[0..1],
89 lea.l 132(%a1), %a6 | %a6 = delay line wrap limit 89 | %a0..%a1 = history[2..3],
90 move.l (%a6), %a0 | fetch delay line address 90 | %a2 = index, %a3 = index_max
91 movem.l (%a1), %a1-%a3 | load filter coefs 91 lea.l 0x28(%a6), %a6 | %a6 = state->delay
92 bra.b 20f | loop start | go to loop start point 92 move.l %a6, -(%sp) | push state->delay
93 bra.b .cfp_loop_start
93 /* Register usage in loop: 94 /* Register usage in loop:
94 * %a0 = delay_p, %a1..%a3 = b0, b1, a1 (filter coefs), 95 * %d0 = count, %d1 = direct gain, %d2..%d4 = b0, b1, a1 (filter coefs),
95 * %a4 = buf[0], %a5 = buf[1], 96 * %d5..%d6 = history[0..1], %d7 = scratch
96 * %a6 = delay line pointer wrap limit, 97 * %a0..%a1 = history[2..3], %a2 = index, %a3 = index_max,
97 * %d0..%d3 = history 98 * %a4 = buf[0], %a5 = buf[1], %a6 = scratch
98 * %d4..%d5 = temp.
99 * %d6 = direct gain,
100 * %d7 = count
101 */ 99 */
10210: | loop | 100.cfp_loop:
103 movclr.l %acc0, %d4 | write outputs 101 movclr.l %acc0, %d7 | write outputs
104 move.l %d4, (%a4)+ | . 102 move.l %d7, (%a4)+ | .
105 movclr.l %acc1, %d5 | . 103 movclr.l %acc1, %a6 | .
106 move.l %d5, (%a5)+ | . 104 move.l %a6, (%a5)+ | .
10720: | loop start | 105.cfp_loop_start:
108 mac.l %a2, %d0, (%a0)+, %d0, %acc0 | %acc0 = b1*dl[n - 1], %d0 = dl[n] 106 mac.l %d3, %d5, (%a2)+, %d5, %acc1 | %acc1 = b1*dl[n - 1], %d5 = dl[n]
109 mac.l %a1, %d0 , %acc0 | %acc0 += b0*dl[n] 107 mac.l %d2, %d5 , %acc1 | %acc1 += b0*dl[n]
110 mac.l %a3, %d1, (%a5), %d5, %acc0 | %acc0 += a1*y_r[n - 1], load R 108 mac.l %d4, %d6, (%a4), %d7, %acc1 | %acc1 += a1*y_l[n - 1], %d7 = x_l[n]
111 mac.l %a2, %d2, (%a0)+, %d2, %acc1 | %acc1 = b1*dr[n - 1], %d2 = dr[n] 109 mac.l %d3, %a0, (%a2)+, %a0, %acc0 | %acc0 = b1*dr[n - 1], %a0 = dr[n]
112 mac.l %a1, %d2 , %acc1 | %acc1 += b0*dr[n] 110 mac.l %a2, %a0 , %acc0 | %acc0 += b0*dr[n]
113 mac.l %a3, %d3, (%a4), %d4, %acc1 | %acc1 += a1*y_l[n - 1], load L 111 mac.l %d4, %a1, (%a5), %a6, %acc0 | %acc0 += a1*y_r[n - 1], %a6 = x_r[n]
114 movem.l %d4-%d5, -8(%a0) | save left & right inputs to delay line 112 movem.l %d7/%a6, -8(%a2) | save x_l[n] and x_r[n] to delay line
115 move.l %acc0, %d3 | get filtered delayed left sample (y_l[n]) 113 move.l %acc1, %d6 | get filtered delayed left sample (y_l[n])
116 move.l %acc1, %d1 | get filtered delayed right sample (y_r[n]) 114 move.l %acc0, %a1 | get filtered delayed right sample (y_r[n])
117 mac.l %d6, %d4, %acc0 | %acc0 += gain*x_l[n] 115 mac.l %d1, %d7, %acc0 | %acc0 = gain*x_l[n] + y_r[n]
118 mac.l %d6, %d5, %acc1 | %acc1 += gain*x_r[n] 116 mac.l %d1, %a6, %acc1 | %acc1 = gain*x_r[n] + y_l[n]
119 cmp.l %a6, %a0 | wrap %a0 if passed end 117
120 bhs.b 30f | wrap buffer | 118 cmp.l %a3, %a2 | wrap index if past end
121 tpf.l | trap the buffer wrap 119 bhs.b 1f |
12230: | wrap buffer | ...fwd taken branches more costly 120 tpf.w | trap the buffer wrap
123 lea.l -104(%a6), %a0 | wrap it up 1211: | ...fwd taken branches more costly
124 subq.l #1, %d7 | --count > 0 ? 122 move.l (%sp), %a2 | 2b | wrap it up
125 bgt.b 10b | loop | yes? do more 123
126 movclr.l %acc0, %d4 | write last outputs 124 subq.l #1, %d0 | --count > 0 ?
127 move.l %d4, (%a4) | . 125 bgt.b .cfp_loop | yes? do more
128 movclr.l %acc1, %d5 | . 126
129 move.l %d5, (%a5) | . 127 movclr.l %acc0, %d7 | write last outputs
130 movem.l %d0-%d3, -120(%a6) | ...history 128 move.l %d7, (%a4) | .
131 move.l %a0, (%a6) | ...delay_p 129 movclr.l %acc1, %a6 | .
130 move.l %a6, (%a5) | .
131
132 move.l (%sp)+, %a6 | pop state->delay
133 movem.l %d5-%d6/%a0-%a2, -0x18(%a6) | save history, index
132 movem.l (%sp), %d2-%d7/%a2-%a6 | restore all regs 134 movem.l (%sp), %d2-%d7/%a2-%a6 | restore all regs
133 lea.l 44(%sp), %sp | 135 lea.l 44(%sp), %sp |
134 rts | 136 rts |
135 .size crossfeed_process,.-crossfeed_process 137 .size crossfeed_process, .-crossfeed_process
136 138
137/**************************************************************************** 139/****************************************************************************
138 * void crossfeed_meier_process(struct dsp_proc_entry *this, 140 * void crossfeed_meier_process(struct dsp_proc_entry *this,
@@ -147,7 +149,7 @@ crossfeed_meier_process:
147 movem.l %d2-%d6/%a2, (%sp) | . 149 movem.l %d2-%d6/%a2, (%sp) | .
148 move.l (%a0), %a0 | %a0 = &this->data = &crossfeed_state 150 move.l (%a0), %a0 | %a0 = &this->data = &crossfeed_state
149 move.l (%a1), %a1 | %a1 = buf = *buf_p 151 move.l (%a1), %a1 | %a1 = buf = *buf_p
150 movem.l 16(%a0), %d1-%d5 | %d1 = vcl, %d2 = vcr, %d3 = vdiff, 152 movem.l 4(%a0), %d1-%d5 | %d1 = vcl, %d2 = vcr, %d3 = vdiff,
151 | %d4 = coef1, %d5 = coef2 153 | %d4 = coef1, %d5 = coef2
152 movem.l (%a1), %d0/%a1-%a2 | %d0 = count = buf->remcount 154 movem.l (%a1), %d0/%a1-%a2 | %d0 = count = buf->remcount
153 | %a1 = p32[0], %a2 = p32[1] 155 | %a1 = p32[0], %a2 = p32[1]
@@ -155,7 +157,7 @@ crossfeed_meier_process:
155 | %d0 = count, %d1 = vcl, %d2 = vcr, %d3 = vdiff/lout, 157 | %d0 = count, %d1 = vcl, %d2 = vcr, %d3 = vdiff/lout,
156 | %d4 = coef1, %d5 = coef2, %d6 = rout/scratch 158 | %d4 = coef1, %d5 = coef2, %d6 = rout/scratch
157 | %a1 = p32[0], %a2 = p32[1] 159 | %a1 = p32[0], %a2 = p32[1]
15810: | loop 160.cfmp_loop:
159 mac.l %d5, %d3, %acc0 | %acc0 = common = coef2*vdiff 161 mac.l %d5, %d3, %acc0 | %acc0 = common = coef2*vdiff
160 move.l %acc0, %acc1 | copy common 162 move.l %acc0, %acc1 | copy common
161 mac.l %d4, %d1, (%a1), %d3, %acc0 | %acc0 += coef1*vcl, %d3 = lout 163 mac.l %d4, %d1, (%a1), %d3, %acc0 | %acc0 += coef1*vcl, %d3 = lout
@@ -170,9 +172,9 @@ crossfeed_meier_process:
170 movclr.l %acc1, %d6 | %d5 = fetch -res2 in s0.31 172 movclr.l %acc1, %d6 | %d5 = fetch -res2 in s0.31
171 add.l %d6, %d2 | vcr += -res2 173 add.l %d6, %d2 | vcr += -res2
172 subq.l #1, %d0 | count-- 174 subq.l #1, %d0 | count--
173 bgt 10b | loop | more samples? 175 bgt .cfmp_loop | more samples?
174 | 176 |
175 movem.l %d1-%d3, 16(%a0) | save vcl, vcr, vdiff 177 movem.l %d1-%d3, 4(%a0) | save vcl, vcr, vdiff
176 movem.l (%sp), %d2-%d6/%a2 | restore non-volatiles 178 movem.l (%sp), %d2-%d6/%a2 | restore non-volatiles
177 lea.l 24(%sp), %sp | . 179 lea.l 24(%sp), %sp | .
178 rts | 180 rts |
diff --git a/lib/rbcodec/dsp/dsp_core.c b/lib/rbcodec/dsp/dsp_core.c
index 871ccbfd23..b0e9c8a304 100644
--- a/lib/rbcodec/dsp/dsp_core.c
+++ b/lib/rbcodec/dsp/dsp_core.c
@@ -103,8 +103,21 @@ static intptr_t proc_broadcast(struct dsp_config *dsp, unsigned int setting,
103 intptr_t value) 103 intptr_t value)
104{ 104{
105 bool multi = setting < DSP_PROC_SETTING; 105 bool multi = setting < DSP_PROC_SETTING;
106 struct dsp_proc_slot *s = multi ? dsp->proc_slots : 106 struct dsp_proc_slot *s;
107 find_proc_slot(dsp, setting - DSP_PROC_SETTING); 107
108 if (multi)
109 {
110 /* Message to all enabled stages */
111 if (dsp_sample_io_configure(&dsp->io_data, setting, &value))
112 return value; /* To I/O only */
113
114 s = dsp->proc_slots;
115 }
116 else
117 {
118 /* Message to a particular stage */
119 s = find_proc_slot(dsp, setting - DSP_PROC_SETTING);
120 }
108 121
109 while (s != NULL) 122 while (s != NULL)
110 { 123 {
@@ -117,7 +130,7 @@ static intptr_t proc_broadcast(struct dsp_config *dsp, unsigned int setting,
117 s = s->next; 130 s = s->next;
118 } 131 }
119 132
120 return multi ? 1 : 0; 133 return 0;
121} 134}
122 135
123/* Add an item to the enabled list */ 136/* Add an item to the enabled list */
@@ -244,6 +257,12 @@ void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id,
244 proc_db_entry(s)->configure(&s->proc_entry, dsp, DSP_PROC_CLOSE, 0); 257 proc_db_entry(s)->configure(&s->proc_entry, dsp, DSP_PROC_CLOSE, 0);
245} 258}
246 259
260/* Is the stage specified by the id currently enabled? */
261bool dsp_proc_enabled(struct dsp_config *dsp, enum dsp_proc_ids id)
262{
263 return (dsp->proc_mask_enabled & BIT_N(id)) != 0;
264}
265
247/* Activate or deactivate a stage */ 266/* Activate or deactivate a stage */
248void dsp_proc_activate(struct dsp_config *dsp, enum dsp_proc_ids id, 267void dsp_proc_activate(struct dsp_config *dsp, enum dsp_proc_ids id,
249 bool activate) 268 bool activate)
@@ -454,7 +473,6 @@ void dsp_process(struct dsp_config *dsp, struct dsp_buffer *src,
454intptr_t dsp_configure(struct dsp_config *dsp, unsigned int setting, 473intptr_t dsp_configure(struct dsp_config *dsp, unsigned int setting,
455 intptr_t value) 474 intptr_t value)
456{ 475{
457 dsp_sample_io_configure(&dsp->io_data, setting, value);
458 return proc_broadcast(dsp, setting, value); 476 return proc_broadcast(dsp, setting, value);
459} 477}
460 478
@@ -497,7 +515,8 @@ void INIT_ATTR dsp_init(void)
497 count = slot_count[i]; 515 count = slot_count[i];
498 dsp->slot_free_mask = MASK_N(uint32_t, count, shift); 516 dsp->slot_free_mask = MASK_N(uint32_t, count, shift);
499 517
500 dsp_sample_io_configure(&dsp->io_data, DSP_INIT, i); 518 intptr_t value = i;
519 dsp_sample_io_configure(&dsp->io_data, DSP_INIT, &value);
501 520
502 /* Notify each db entry of global init for each DSP */ 521 /* Notify each db entry of global init for each DSP */
503 for (unsigned int j = 0; j < DSP_NUM_PROC_STAGES; j++) 522 for (unsigned int j = 0; j < DSP_NUM_PROC_STAGES; j++)
diff --git a/lib/rbcodec/dsp/dsp_core.h b/lib/rbcodec/dsp/dsp_core.h
index d3cfdd133c..0f63b62e00 100644
--- a/lib/rbcodec/dsp/dsp_core.h
+++ b/lib/rbcodec/dsp/dsp_core.h
@@ -39,14 +39,14 @@ enum dsp_settings
39 DSP_SET_STEREO_MODE, 39 DSP_SET_STEREO_MODE,
40 DSP_FLUSH, 40 DSP_FLUSH,
41 DSP_SET_PITCH, 41 DSP_SET_PITCH,
42 DSP_SET_OUT_FREQUENCY,
43 DSP_GET_OUT_FREQUENCY,
42 DSP_PROC_INIT, 44 DSP_PROC_INIT,
43 DSP_PROC_CLOSE, 45 DSP_PROC_CLOSE,
44 DSP_PROC_NEW_FORMAT, 46 DSP_PROC_NEW_FORMAT,
45 DSP_PROC_SETTING, /* stage-specific should be this + id */ 47 DSP_PROC_SETTING, /* stage-specific should be this + id */
46}; 48};
47 49
48#define NATIVE_FREQUENCY 44100 /* internal/output sample rate */
49
50enum dsp_stereo_modes 50enum dsp_stereo_modes
51{ 51{
52 STEREO_INTERLEAVED, 52 STEREO_INTERLEAVED,
diff --git a/lib/rbcodec/dsp/dsp_misc.c b/lib/rbcodec/dsp/dsp_misc.c
index cc74a790ea..ad6f5b5b31 100644
--- a/lib/rbcodec/dsp/dsp_misc.c
+++ b/lib/rbcodec/dsp/dsp_misc.c
@@ -134,6 +134,21 @@ int32_t dsp_get_pitch(void)
134} 134}
135#endif /* HAVE_PITCHCONTROL */ 135#endif /* HAVE_PITCHCONTROL */
136 136
137/* Set output samplerate for all DSPs */
138void dsp_set_all_output_frequency(unsigned int samplerate)
139{
140
141 struct dsp_config *dsp;
142 for (int i = 0; (dsp = dsp_get_config(i)); i++)
143 dsp_configure(dsp, DSP_SET_OUT_FREQUENCY, samplerate);
144}
145
146/* Return DSP's output samplerate */
147unsigned int dsp_get_output_frequency(struct dsp_config *dsp)
148{
149 return dsp_configure(dsp, DSP_GET_OUT_FREQUENCY, 0);
150}
151
137static void INIT_ATTR misc_dsp_init(struct dsp_config *dsp, 152static void INIT_ATTR misc_dsp_init(struct dsp_config *dsp,
138 enum dsp_ids dsp_id) 153 enum dsp_ids dsp_id)
139{ 154{
diff --git a/lib/rbcodec/dsp/dsp_misc.h b/lib/rbcodec/dsp/dsp_misc.h
index 2fed9400f2..af259bfa3e 100644
--- a/lib/rbcodec/dsp/dsp_misc.h
+++ b/lib/rbcodec/dsp/dsp_misc.h
@@ -59,4 +59,11 @@ void dsp_set_pitch(int32_t pitch);
59int32_t dsp_get_pitch(void); 59int32_t dsp_get_pitch(void);
60#endif /* HAVE_PITCHCONTROL */ 60#endif /* HAVE_PITCHCONTROL */
61 61
62/* Set output samplerate for all DSPs */
63void dsp_set_all_output_frequency(unsigned int samplerate);
64
65/* Return DSP's output samplerate */
66struct dsp_config;
67unsigned int dsp_get_output_frequency(struct dsp_config *dsp);
68
62#endif /* DSP_MISC_H */ 69#endif /* DSP_MISC_H */
diff --git a/lib/rbcodec/dsp/dsp_proc_database.h b/lib/rbcodec/dsp/dsp_proc_database.h
index c4c93ef2d9..534c165eef 100644
--- a/lib/rbcodec/dsp/dsp_proc_database.h
+++ b/lib/rbcodec/dsp/dsp_proc_database.h
@@ -40,7 +40,7 @@ DSP_PROC_DB_START
40#ifdef HAVE_PITCHCONTROL 40#ifdef HAVE_PITCHCONTROL
41 DSP_PROC_DB_ITEM(TIMESTRETCH) /* time-stretching */ 41 DSP_PROC_DB_ITEM(TIMESTRETCH) /* time-stretching */
42#endif 42#endif
43 DSP_PROC_DB_ITEM(RESAMPLE) /* resampler providing NATIVE_FREQUENCY */ 43 DSP_PROC_DB_ITEM(RESAMPLE) /* resampler providing output frequency */
44 DSP_PROC_DB_ITEM(CROSSFEED) /* stereo crossfeed */ 44 DSP_PROC_DB_ITEM(CROSSFEED) /* stereo crossfeed */
45 DSP_PROC_DB_ITEM(EQUALIZER) /* n-band equalizer */ 45 DSP_PROC_DB_ITEM(EQUALIZER) /* n-band equalizer */
46#ifdef HAVE_SW_TONE_CONTROLS 46#ifdef HAVE_SW_TONE_CONTROLS
diff --git a/lib/rbcodec/dsp/dsp_proc_entry.h b/lib/rbcodec/dsp/dsp_proc_entry.h
index 902385f08d..1bf59dd73a 100644
--- a/lib/rbcodec/dsp/dsp_proc_entry.h
+++ b/lib/rbcodec/dsp/dsp_proc_entry.h
@@ -132,6 +132,10 @@ typedef intptr_t (*dsp_proc_config_fn_type)(struct dsp_proc_entry *this,
132 * by processing code! */ 132 * by processing code! */
133void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id, 133void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id,
134 bool enable); 134 bool enable);
135
136/* Is the specified stage enabled on the DSP? */
137bool dsp_proc_enabled(struct dsp_config *dsp, enum dsp_proc_ids id);
138
135/* Activate/deactivate processing stage, doesn't affect enabled status 139/* Activate/deactivate processing stage, doesn't affect enabled status
136 * thus will not enable anything - 140 * thus will not enable anything -
137 * may be called during processing to activate/deactivate for format 141 * may be called during processing to activate/deactivate for format
diff --git a/lib/rbcodec/dsp/dsp_sample_input.c b/lib/rbcodec/dsp/dsp_sample_input.c
index 561cb36d9e..537a659b73 100644
--- a/lib/rbcodec/dsp/dsp_sample_input.c
+++ b/lib/rbcodec/dsp/dsp_sample_input.c
@@ -286,14 +286,17 @@ static void INIT_ATTR dsp_sample_input_init(struct sample_io_data *this,
286static void INIT_ATTR dsp_sample_io_init(struct sample_io_data *this, 286static void INIT_ATTR dsp_sample_io_init(struct sample_io_data *this,
287 enum dsp_ids dsp_id) 287 enum dsp_ids dsp_id)
288{ 288{
289 this->output_sampr = DSP_OUT_DEFAULT_HZ;
289 dsp_sample_input_init(this, dsp_id); 290 dsp_sample_input_init(this, dsp_id);
290 dsp_sample_output_init(this); 291 dsp_sample_output_init(this);
291} 292}
292 293
293void dsp_sample_io_configure(struct sample_io_data *this, 294bool dsp_sample_io_configure(struct sample_io_data *this,
294 unsigned int setting, 295 unsigned int setting,
295 intptr_t value) 296 intptr_t *value_p)
296{ 297{
298 intptr_t value = *value_p;
299
297 switch (setting) 300 switch (setting)
298 { 301 {
299 case DSP_INIT: 302 case DSP_INIT:
@@ -306,15 +309,15 @@ void dsp_sample_io_configure(struct sample_io_data *this,
306 this->format.num_channels = 2; 309 this->format.num_channels = 2;
307 this->format.frac_bits = WORD_FRACBITS; 310 this->format.frac_bits = WORD_FRACBITS;
308 this->format.output_scale = WORD_FRACBITS + 1 - NATIVE_DEPTH; 311 this->format.output_scale = WORD_FRACBITS + 1 - NATIVE_DEPTH;
309 this->format.frequency = NATIVE_FREQUENCY; 312 this->format.frequency = this->output_sampr;
310 this->format.codec_frequency = NATIVE_FREQUENCY; 313 this->format.codec_frequency = this->output_sampr;
311 this->sample_depth = NATIVE_DEPTH; 314 this->sample_depth = NATIVE_DEPTH;
312 this->stereo_mode = STEREO_NONINTERLEAVED; 315 this->stereo_mode = STEREO_NONINTERLEAVED;
313 break; 316 break;
314 317
315 case DSP_SET_FREQUENCY: 318 case DSP_SET_FREQUENCY:
316 format_change_set(this); 319 format_change_set(this);
317 value = value > 0 ? value : NATIVE_FREQUENCY; 320 value = value > 0 ? (unsigned int)value : this->output_sampr;
318 this->format.frequency = value; 321 this->format.frequency = value;
319 this->format.codec_frequency = value; 322 this->format.codec_frequency = value;
320 break; 323 break;
@@ -345,5 +348,22 @@ void dsp_sample_io_configure(struct sample_io_data *this,
345 this->format.frequency = 348 this->format.frequency =
346 fp_mul(value, this->format.codec_frequency, 16); 349 fp_mul(value, this->format.codec_frequency, 16);
347 break; 350 break;
351
352 case DSP_SET_OUT_FREQUENCY:
353 value = value > 0 ? value : DSP_OUT_DEFAULT_HZ;
354 value = MIN(DSP_OUT_MAX_HZ, MAX(DSP_OUT_MIN_HZ, value));
355 *value_p = value;
356
357 if ((unsigned int)value == this->output_sampr)
358 return true; /* No change; don't broadcast */
359
360 this->output_sampr = value;
361 break;
362
363 case DSP_GET_OUT_FREQUENCY:
364 *value_p = this->output_sampr;
365 return true; /* Only I/O handles it */
348 } 366 }
367
368 return false;
349} 369}
diff --git a/lib/rbcodec/dsp/dsp_sample_io.h b/lib/rbcodec/dsp/dsp_sample_io.h
index 22b7a4a3f4..5117e04a3e 100644
--- a/lib/rbcodec/dsp/dsp_sample_io.h
+++ b/lib/rbcodec/dsp/dsp_sample_io.h
@@ -50,6 +50,7 @@ struct sample_io_data
50 struct dsp_buffer sample_buf; /* Buffer descriptor for converted samples */ 50 struct dsp_buffer sample_buf; /* Buffer descriptor for converted samples */
51 int32_t *sample_buf_p[2]; /* Internal format buffer pointers */ 51 int32_t *sample_buf_p[2]; /* Internal format buffer pointers */
52 sample_output_fn_type output_samples; /* Final output function */ 52 sample_output_fn_type output_samples; /* Final output function */
53 unsigned int output_sampr; /* Master output samplerate */
53 uint8_t format_dirty; /* Format change set, avoids superfluous 54 uint8_t format_dirty; /* Format change set, avoids superfluous
54 increments before carrying it out */ 55 increments before carrying it out */
55 uint8_t output_version; /* Format version of src buffer at output */ 56 uint8_t output_version; /* Format version of src buffer at output */
@@ -62,8 +63,8 @@ void dsp_sample_output_format_change(struct sample_io_data *this,
62 struct sample_format *format); 63 struct sample_format *format);
63 64
64/* Sample IO watches the format setting from the codec */ 65/* Sample IO watches the format setting from the codec */
65void dsp_sample_io_configure(struct sample_io_data *this, 66bool dsp_sample_io_configure(struct sample_io_data *this,
66 unsigned int setting, 67 unsigned int setting,
67 intptr_t value); 68 intptr_t *value_p);
68 69
69#endif /* DSP_SAMPLE_IO_H */ 70#endif /* DSP_SAMPLE_IO_H */
diff --git a/lib/rbcodec/dsp/eq.c b/lib/rbcodec/dsp/eq.c
index 94cb61deec..e4d7bf5e02 100644
--- a/lib/rbcodec/dsp/eq.c
+++ b/lib/rbcodec/dsp/eq.c
@@ -25,6 +25,7 @@
25#include "dsp_filter.h" 25#include "dsp_filter.h"
26#include "dsp_proc_entry.h" 26#include "dsp_proc_entry.h"
27#include "dsp_core.h" 27#include "dsp_core.h"
28#include "dsp_misc.h"
28#include "eq.h" 29#include "eq.h"
29#include "pga.h" 30#include "pga.h"
30#include "replaygain.h" 31#include "replaygain.h"
@@ -42,6 +43,9 @@
42#error Band count must be greater than or equal to 3 43#error Band count must be greater than or equal to 3
43#endif 44#endif
44 45
46/* Cached band settings */
47static struct eq_band_setting settings[EQ_NUM_BANDS];
48
45static struct eq_state 49static struct eq_state
46{ 50{
47 uint32_t enabled; /* Mask of enabled bands */ 51 uint32_t enabled; /* Mask of enabled bands */
@@ -49,16 +53,48 @@ static struct eq_state
49 struct dsp_filter filters[EQ_NUM_BANDS]; /* Data for each filter */ 53 struct dsp_filter filters[EQ_NUM_BANDS]; /* Data for each filter */
50} eq_data IBSS_ATTR; 54} eq_data IBSS_ATTR;
51 55
56#define FOR_EACH_ENB_BAND(b) \
57 for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++)
58
52/* Clear histories of all enabled bands */ 59/* Clear histories of all enabled bands */
53static void eq_flush(void) 60static void eq_flush(void)
54{ 61{
55 if (eq_data.enabled == 0) 62 if (eq_data.enabled == 0)
56 return; /* Not initialized yet/no bands on */ 63 return; /* Not initialized yet/no bands on */
57 64
58 for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++) 65 FOR_EACH_ENB_BAND(b)
59 filter_flush(&eq_data.filters[*b]); 66 filter_flush(&eq_data.filters[*b]);
60} 67}
61 68
69static void update_band_filter(int band, unsigned int fout)
70{
71 /* Convert user settings to format required by coef generator
72 functions */
73 typeof (filter_pk_coefs) *coef_gen = filter_pk_coefs;
74
75 /* Only first and last bands are not peaking filters */
76 if (band == 0)
77 coef_gen = filter_ls_coefs;
78 else if (band == EQ_NUM_BANDS-1)
79 coef_gen = filter_hs_coefs;
80
81 const struct eq_band_setting *setting = &settings[band];
82 struct dsp_filter *filter = &eq_data.filters[band];
83
84 coef_gen(fp_div(setting->cutoff, fout, 32), setting->q ?: 1,
85 setting->gain, filter);
86}
87
88/* Resync all bands to a new DSP output frequency */
89static void update_samplerate(unsigned int fout)
90{
91 if (eq_data.enabled == 0)
92 return; /* Not initialized yet/no bands on */
93
94 FOR_EACH_ENB_BAND(b)
95 update_band_filter(*b, fout);
96}
97
62/** DSP interface **/ 98/** DSP interface **/
63 99
64/* Set the precut gain value */ 100/* Set the precut gain value */
@@ -73,11 +109,14 @@ void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting)
73 if (band < 0 || band >= EQ_NUM_BANDS) 109 if (band < 0 || band >= EQ_NUM_BANDS)
74 return; 110 return;
75 111
112 settings[band] = *setting; /* cache setting */
113
114 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
115
76 /* NOTE: The coef functions assume the EMAC unit is in fractional mode, 116 /* NOTE: The coef functions assume the EMAC unit is in fractional mode,
77 which it should be, since we're executed from the main thread. */ 117 which it should be, since we're executed from the main thread. */
78 118
79 uint32_t mask = eq_data.enabled; 119 uint32_t mask = eq_data.enabled;
80 struct dsp_filter *filter = &eq_data.filters[band];
81 120
82 /* Assume a band is disabled if the gain is zero */ 121 /* Assume a band is disabled if the gain is zero */
83 mask &= ~BIT_N(band); 122 mask &= ~BIT_N(band);
@@ -85,33 +124,19 @@ void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting)
85 if (setting->gain != 0) 124 if (setting->gain != 0)
86 { 125 {
87 mask |= BIT_N(band); 126 mask |= BIT_N(band);
88 127 update_band_filter(band, dsp_get_output_frequency(dsp));
89 /* Convert user settings to format required by coef generator
90 functions */
91 void (* coef_gen)(unsigned long cutoff, unsigned long Q, long db,
92 struct dsp_filter *f) = filter_pk_coefs;
93
94 /* Only first and last bands are not peaking filters */
95 if (band == 0)
96 coef_gen = filter_ls_coefs;
97 else if (band == EQ_NUM_BANDS-1)
98 coef_gen = filter_hs_coefs;
99
100 coef_gen(0xffffffff / NATIVE_FREQUENCY * setting->cutoff,
101 setting->q ?: 1, setting->gain, filter);
102 } 128 }
103 129
104 if (mask == eq_data.enabled) 130 if (mask == eq_data.enabled)
105 return; /* No change in band-enable state */ 131 return; /* No change in band-enable state */
106 132
107 if (mask & BIT_N(band)) 133 if (mask & BIT_N(band))
108 filter_flush(filter); /* Coming online */ 134 filter_flush(&eq_data.filters[band]); /* Coming online */
109 135
110 eq_data.enabled = mask; 136 eq_data.enabled = mask;
111 137
112 /* Only be active if there are bands to process - if EQ is off, then 138 /* Only be active if there are bands to process - if EQ is off, then
113 this call has no effect */ 139 this call has no effect */
114 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
115 dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, mask != 0); 140 dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, mask != 0);
116 141
117 /* Prepare list of enabled bands for efficient iteration */ 142 /* Prepare list of enabled bands for efficient iteration */
@@ -125,6 +150,11 @@ void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting)
125void dsp_eq_enable(bool enable) 150void dsp_eq_enable(bool enable)
126{ 151{
127 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); 152 struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO);
153 bool enabled = dsp_proc_enabled(dsp, DSP_PROC_EQUALIZER);
154
155 if (enable == enabled)
156 return;
157
128 dsp_proc_enable(dsp, DSP_PROC_EQUALIZER, enable); 158 dsp_proc_enable(dsp, DSP_PROC_EQUALIZER, enable);
129 159
130 if (enable && eq_data.enabled != 0) 160 if (enable && eq_data.enabled != 0)
@@ -139,7 +169,7 @@ static void eq_process(struct dsp_proc_entry *this,
139 int count = buf->remcount; 169 int count = buf->remcount;
140 unsigned int channels = buf->format.num_channels; 170 unsigned int channels = buf->format.num_channels;
141 171
142 for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++) 172 FOR_EACH_ENB_BAND(b)
143 filter_process(&eq_data.filters[*b], buf->p32, count, channels); 173 filter_process(&eq_data.filters[*b], buf->p32, count, channels);
144 174
145 (void)this; 175 (void)this;
@@ -154,10 +184,9 @@ static intptr_t eq_configure(struct dsp_proc_entry *this,
154 switch (setting) 184 switch (setting)
155 { 185 {
156 case DSP_PROC_INIT: 186 case DSP_PROC_INIT:
157 if (value != 0)
158 break; /* Already enabled */
159
160 this->process = eq_process; 187 this->process = eq_process;
188 /* Wouldn't have been getting frequency updates */
189 update_samplerate(dsp_get_output_frequency(dsp));
161 /* Fall-through */ 190 /* Fall-through */
162 case DSP_PROC_CLOSE: 191 case DSP_PROC_CLOSE:
163 pga_enable_gain(PGA_EQ_PRECUT, setting == DSP_PROC_INIT); 192 pga_enable_gain(PGA_EQ_PRECUT, setting == DSP_PROC_INIT);
@@ -166,6 +195,10 @@ static intptr_t eq_configure(struct dsp_proc_entry *this,
166 case DSP_FLUSH: 195 case DSP_FLUSH:
167 eq_flush(); 196 eq_flush();
168 break; 197 break;
198
199 case DSP_SET_OUT_FREQUENCY:
200 update_samplerate(value);
201 break;
169 } 202 }
170 203
171 return 0; 204 return 0;
diff --git a/lib/rbcodec/dsp/resample.c b/lib/rbcodec/dsp/resample.c
index 6e7e5b7b45..0a97bdf70c 100644
--- a/lib/rbcodec/dsp/resample.c
+++ b/lib/rbcodec/dsp/resample.c
@@ -25,6 +25,7 @@
25#include "fracmul.h" 25#include "fracmul.h"
26#include "fixedpoint.h" 26#include "fixedpoint.h"
27#include "dsp_proc_entry.h" 27#include "dsp_proc_entry.h"
28#include "dsp_misc.h"
28#include <string.h> 29#include <string.h>
29 30
30/** 31/**
@@ -50,9 +51,10 @@ static struct resample_data
50 int32_t history[2][3]; /* 08h: Last samples for interpolation (L+R) 51 int32_t history[2][3]; /* 08h: Last samples for interpolation (L+R)
51 0 = oldest, 2 = newest */ 52 0 = oldest, 2 = newest */
52 /* 20h */ 53 /* 20h */
53 int32_t frequency; /* Virtual samplerate */ 54 unsigned int frequency; /* Virtual input samplerate */
55 unsigned int frequency_out; /* Resampler output samplerate */
54 struct dsp_buffer resample_buf; /* Buffer descriptor for resampled data */ 56 struct dsp_buffer resample_buf; /* Buffer descriptor for resampled data */
55 int32_t *resample_out_p[2]; /* Actual output buffer pointers */ 57 int32_t *resample_out_p[2]; /* Actual output buffer pointers */
56} resample_data[DSP_COUNT] IBSS_ATTR; 58} resample_data[DSP_COUNT] IBSS_ATTR;
57 59
58/* Actual worker function. Implemented here or in target assembly code. */ 60/* Actual worker function. Implemented here or in target assembly code. */
@@ -73,14 +75,16 @@ static void resample_flush(struct dsp_proc_entry *this)
73} 75}
74 76
75static bool resample_new_delta(struct resample_data *data, 77static bool resample_new_delta(struct resample_data *data,
76 struct sample_format *format) 78 struct sample_format *format,
79 unsigned int fout)
77{ 80{
78 int32_t frequency = format->frequency; /* virtual samplerate */ 81 unsigned int frequency = format->frequency; /* virtual samplerate */
79 82
80 data->frequency = frequency; 83 data->frequency = frequency;
81 data->delta = fp_div(frequency, NATIVE_FREQUENCY, 16); 84 data->frequency_out = fout;
85 data->delta = fp_div(frequency, fout, 16);
82 86
83 if (frequency == NATIVE_FREQUENCY) 87 if (frequency == data->frequency_out)
84 { 88 {
85 /* NOTE: If fully glitch-free transistions from no resampling to 89 /* NOTE: If fully glitch-free transistions from no resampling to
86 resampling are desired, history should be maintained even when 90 resampling are desired, history should be maintained even when
@@ -232,20 +236,23 @@ static intptr_t resample_new_format(struct dsp_proc_entry *this,
232 236
233 DSP_PRINT_FORMAT(DSP_PROC_RESAMPLE, *format); 237 DSP_PRINT_FORMAT(DSP_PROC_RESAMPLE, *format);
234 238
235 int32_t frequency = data->frequency; 239 unsigned int frequency = data->frequency;
240 unsigned int fout = dsp_get_output_frequency(dsp);
236 bool active = dsp_proc_active(dsp, DSP_PROC_RESAMPLE); 241 bool active = dsp_proc_active(dsp, DSP_PROC_RESAMPLE);
237 242
238 if (format->frequency != frequency) 243 if ((unsigned int)format->frequency != frequency ||
244 data->frequency_out != fout)
239 { 245 {
240 DEBUGF(" DSP_PROC_RESAMPLE- new delta\n"); 246 DEBUGF(" DSP_PROC_RESAMPLE- new settings: %u %u\n",
241 active = resample_new_delta(data, format); 247 format->frequency, fout);
248 active = resample_new_delta(data, format, fout);
242 dsp_proc_activate(dsp, DSP_PROC_RESAMPLE, active); 249 dsp_proc_activate(dsp, DSP_PROC_RESAMPLE, active);
243 } 250 }
244 251
245 /* Everything after us is NATIVE_FREQUENCY */ 252 /* Everything after us is fout */
246 dst->format = *format; 253 dst->format = *format;
247 dst->format.frequency = NATIVE_FREQUENCY; 254 dst->format.frequency = fout;
248 dst->format.codec_frequency = NATIVE_FREQUENCY; 255 dst->format.codec_frequency = fout;
249 256
250 if (active) 257 if (active)
251 return PROC_NEW_FORMAT_OK; 258 return PROC_NEW_FORMAT_OK;
@@ -287,8 +294,10 @@ static void INIT_ATTR resample_dsp_init(struct dsp_config *dsp,
287static void INIT_ATTR resample_proc_init(struct dsp_proc_entry *this, 294static void INIT_ATTR resample_proc_init(struct dsp_proc_entry *this,
288 struct dsp_config *dsp) 295 struct dsp_config *dsp)
289{ 296{
297 struct resample_data *data = &resample_data[dsp_get_id(dsp)];
298 this->data = (intptr_t)data;
290 dsp_proc_set_in_place(dsp, DSP_PROC_RESAMPLE, false); 299 dsp_proc_set_in_place(dsp, DSP_PROC_RESAMPLE, false);
291 this->data = (intptr_t)&resample_data[dsp_get_id(dsp)]; 300 data->frequency_out = DSP_OUT_DEFAULT_HZ;
292 this->process = resample_process; 301 this->process = resample_process;
293} 302}
294 303
@@ -322,6 +331,10 @@ static intptr_t resample_configure(struct dsp_proc_entry *this,
322 case DSP_PROC_NEW_FORMAT: 331 case DSP_PROC_NEW_FORMAT:
323 retval = resample_new_format(this, dsp, (struct sample_format *)value); 332 retval = resample_new_format(this, dsp, (struct sample_format *)value);
324 break; 333 break;
334
335 case DSP_SET_OUT_FREQUENCY:
336 dsp_proc_want_format_update(dsp, DSP_PROC_RESAMPLE);
337 break;
325 } 338 }
326 339
327 return retval; 340 return retval;
diff --git a/lib/rbcodec/dsp/tone_controls.c b/lib/rbcodec/dsp/tone_controls.c
index e636b04b9a..4266af4d97 100644
--- a/lib/rbcodec/dsp/tone_controls.c
+++ b/lib/rbcodec/dsp/tone_controls.c
@@ -21,9 +21,11 @@
21 ****************************************************************************/ 21 ****************************************************************************/
22#include "rbcodecconfig.h" 22#include "rbcodecconfig.h"
23#include "platform.h" 23#include "platform.h"
24#include "fixedpoint.h"
24#include "dsp_proc_entry.h" 25#include "dsp_proc_entry.h"
25#include "dsp_filter.h" 26#include "dsp_filter.h"
26#include "tone_controls.h" 27#include "tone_controls.h"
28#include "dsp_misc.h"
27 29
28/* These apply to all DSP streams to remain as consistant as possible with 30/* These apply to all DSP streams to remain as consistant as possible with
29 * the behavior of hardware tone controls */ 31 * the behavior of hardware tone controls */
@@ -36,32 +38,39 @@ static const unsigned int tone_treble_cutoff = 3500;
36static int tone_bass = 0; 38static int tone_bass = 0;
37static int tone_treble = 0; 39static int tone_treble = 0;
38 40
41/* Current prescaler setting */
42static int tone_prescale = 0;
43
39/* Data for each DSP */ 44/* Data for each DSP */
40static struct dsp_filter tone_filters[DSP_COUNT] IBSS_ATTR; 45static struct dsp_filter tone_filters[DSP_COUNT] IBSS_ATTR;
41 46
47static void update_filter(int id, unsigned int fout)
48{
49 filter_bishelf_coefs(fp_div(tone_bass_cutoff, fout, 32),
50 fp_div(tone_treble_cutoff, fout, 32),
51 tone_bass, tone_treble, -tone_prescale,
52 &tone_filters[id]);
53}
54
42/* Update the filters' coefficients based upon the bass/treble settings */ 55/* Update the filters' coefficients based upon the bass/treble settings */
43void tone_set_prescale(int prescale) 56void tone_set_prescale(int prescale)
44{ 57{
45 int bass = tone_bass; 58 int bass = tone_bass;
46 int treble = tone_treble; 59 int treble = tone_treble;
47 60
48 struct dsp_filter tone_filter; /* Temp to hold new version */ 61 tone_prescale = prescale;
49 filter_bishelf_coefs(0xffffffff / NATIVE_FREQUENCY * tone_bass_cutoff,
50 0xffffffff / NATIVE_FREQUENCY * tone_treble_cutoff,
51 bass, treble, -prescale, &tone_filter);
52 62
53 struct dsp_config *dsp; 63 struct dsp_config *dsp;
54 for (int i = 0; (dsp = dsp_get_config(i)); i++) 64 for (int i = 0; (dsp = dsp_get_config(i)); i++)
55 { 65 {
56 struct dsp_filter *filter = &tone_filters[i]; 66 update_filter(i, dsp_get_output_frequency(dsp));
57 filter_copy(filter, &tone_filter);
58 67
59 bool enable = bass != 0 || treble != 0; 68 bool enable = bass != 0 || treble != 0;
60 dsp_proc_enable(dsp, DSP_PROC_TONE_CONTROLS, enable); 69 dsp_proc_enable(dsp, DSP_PROC_TONE_CONTROLS, enable);
61 70
62 if (!dsp_proc_active(dsp, DSP_PROC_TONE_CONTROLS)) 71 if (enable && !dsp_proc_active(dsp, DSP_PROC_TONE_CONTROLS))
63 { 72 {
64 filter_flush(filter); /* Going online */ 73 filter_flush(&tone_filters[i]); /* Going online */
65 dsp_proc_activate(dsp, DSP_PROC_TONE_CONTROLS, true); 74 dsp_proc_activate(dsp, DSP_PROC_TONE_CONTROLS, true);
66 } 75 }
67 } 76 }
@@ -109,6 +118,10 @@ static intptr_t tone_configure(struct dsp_proc_entry *this,
109 case DSP_FLUSH: 118 case DSP_FLUSH:
110 filter_flush((struct dsp_filter *)this->data); 119 filter_flush((struct dsp_filter *)this->data);
111 break; 120 break;
121
122 case DSP_SET_OUT_FREQUENCY:
123 update_filter(dsp_get_id(dsp), value);
124 break;
112 } 125 }
113 126
114 return 0; 127 return 0;
diff --git a/lib/rbcodec/rbcodecconfig-example.h b/lib/rbcodec/rbcodecconfig-example.h
index 7ecbc1e96f..e5da42feba 100644
--- a/lib/rbcodec/rbcodecconfig-example.h
+++ b/lib/rbcodec/rbcodecconfig-example.h
@@ -5,6 +5,10 @@
5#define HAVE_SW_TONE_CONTROLS 5#define HAVE_SW_TONE_CONTROLS
6#define HAVE_ALBUMART 6#define HAVE_ALBUMART
7#define NUM_CORES 1 7#define NUM_CORES 1
8/* All the same unless a configuration option is added to warble */
9#define DSP_OUT_MIN_HZ 44100
10#define DSP_OUT_DEFAULT_HZ 44100
11#define DSP_OUT_MAX_HZ 44100
8 12
9#ifndef __ASSEMBLER__ 13#ifndef __ASSEMBLER__
10 14
diff --git a/lib/rbcodec/test/warble.c b/lib/rbcodec/test/warble.c
index 735fa2511f..336438374c 100644
--- a/lib/rbcodec/test/warble.c
+++ b/lib/rbcodec/test/warble.c
@@ -145,7 +145,7 @@ static void write_wav_header(void)
145 if (use_dsp) { 145 if (use_dsp) {
146 channels = 2; 146 channels = 2;
147 sample_size = 16; 147 sample_size = 16;
148 freq = NATIVE_FREQUENCY; 148 freq = dsp_get_output_frequency(ci.dsp);
149 type = WAVE_FORMAT_PCM; 149 type = WAVE_FORMAT_PCM;
150 } else { 150 } else {
151 channels = format.channels; 151 channels = format.channels;
@@ -312,7 +312,7 @@ static void playback_start(void)
312{ 312{
313 playback_running = true; 313 playback_running = true;
314 SDL_AudioSpec spec = {0}; 314 SDL_AudioSpec spec = {0};
315 spec.freq = NATIVE_FREQUENCY; 315 spec.freq = dsp_get_output_frequency(ci.dsp);
316 spec.format = AUDIO_S16SYS; 316 spec.format = AUDIO_S16SYS;
317 spec.channels = 2; 317 spec.channels = 2;
318 spec.samples = 0x400; 318 spec.samples = 0x400;
@@ -776,6 +776,7 @@ static void decode_file(const char *input_fn)
776 ci.id3 = &id3; 776 ci.id3 = &id3;
777 if (use_dsp) { 777 if (use_dsp) {
778 ci.dsp = dsp_get_config(CODEC_IDX_AUDIO); 778 ci.dsp = dsp_get_config(CODEC_IDX_AUDIO);
779 dsp_configure(ci.dsp, DSP_SET_OUT_FREQUENCY, DSP_OUT_DEFAULT_HZ);
779 dsp_configure(ci.dsp, DSP_RESET, 0); 780 dsp_configure(ci.dsp, DSP_RESET, 0);
780 dsp_dither_enable(false); 781 dsp_dither_enable(false);
781 } 782 }