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 @@ #include "config.h" #include "system.h" #include "settings.h" -#include "dsp_core.h" /* for NATIVE_FREQUENCY */ #include "pcm.h" #include "pcm_mixer.h" #include "misc.h" +#include "fixedpoint.h" /** Beep generation, CPU optimized **/ #include "asm/beep.c" @@ -39,8 +39,10 @@ static uint32_t beep_amplitude; /* Amplitude of square wave generator */ #endif static int beep_count; /* Number of samples remaining to generate */ -/* Reserve enough static space for keyclick to fit */ -#define BEEP_BUF_COUNT (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION) +#define BEEP_COUNT(fs, duration) ((fs) / 1000 * (duration)) + +/* Reserve enough static space for keyclick to fit in worst case */ +#define BEEP_BUF_COUNT BEEP_COUNT(PLAY_SAMPR_MAX, KEYCLICK_DURATION) static int16_t beep_buf[BEEP_BUF_COUNT*2] IBSS_ATTR __attribute__((aligned(4))); /* 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, amplitude = INT16_MAX; /* Setup the parameters for the square wave generator */ + uint32_t fout = mixer_get_frequency(); beep_phase = 0; - beep_step = 0xffffffffu / NATIVE_FREQUENCY * frequency; - beep_count = NATIVE_FREQUENCY / 1000 * duration; + beep_step = fp_div(frequency, fout, 32); + beep_count = BEEP_COUNT(fout, duration); #ifdef BEEP_GENERIC 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) codec_queue_ack(Q_CODEC_RUN); trigger_cpu_boost(); + dsp_configure(ci.dsp, DSP_SET_OUT_FREQUENCY, pcmbuf_get_frequency()); if (!encoder) { 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 #if defined(HAVE_HARDWARE_CLICK) hardware_click #endif + +#if defined(HAVE_PLAY_FREQ) +play_frequency +#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 @@ *: "Slow" + + id: LANG_PLAYBACK_FREQUENCY + desc: in playback settings (merge with LANG_RECORDING_FREQUENCY if cleaning) + user: core + + *: none + play_frequency: "Frequency" + + + *: none + play_frequency: "Frequency" + + + *: none + play_frequency: "Frequency" + + 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 @@ #include "misc.h" #if CONFIG_CODEC == SWCODEC #include "playback.h" +#include "pcm_sampr.h" +#ifdef HAVE_PLAY_FREQ +#include "talk.h" +#endif #endif @@ -192,6 +196,10 @@ MENUITEM_SETTING(prevent_skip, &global_settings.prevent_skip, NULL); MENUITEM_SETTING(resume_rewind, &global_settings.resume_rewind, NULL); #endif MENUITEM_SETTING(pause_rewind, &global_settings.pause_rewind, NULL); +#ifdef HAVE_PLAY_FREQ +MENUITEM_SETTING(play_frequency, &global_settings.play_frequency, + playback_callback); +#endif MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0, Icon_Playback_menu, @@ -217,12 +225,15 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0, #ifdef HAVE_HEADPHONE_DETECTION ,&unplug_menu #endif - ,&skip_length, &prevent_skip, + ,&skip_length, &prevent_skip #if CONFIG_CODEC == SWCODEC - &resume_rewind, + ,&resume_rewind +#endif + ,&pause_rewind +#ifdef HAVE_PLAY_FREQ + ,&play_frequency #endif - &pause_rewind, ); static 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) break; case ACTION_EXIT_MENUITEM: /* on exit */ + /* Playing or not */ +#ifdef HAVE_PLAY_FREQ + if (this_item == &play_frequency) + { + settings_apply_play_freq(global_settings.play_frequency, false); + break; + } +#endif /* HAVE_PLAY_FREQ */ + if (!(audio_status() & AUDIO_STATUS_PLAY)) break; + /* Playing only */ if (this_item == &shuffle_item) { 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 @@ #include "settings.h" #include "audio.h" #include "voice_thread.h" -#include "dsp_core.h" /* This is the target fill size of chunks on the pcm buffer Can be any number of samples but power of two sizes make for faster and @@ -66,11 +65,11 @@ chunks */ /* Return data level in 1/4-second increments */ -#define DATA_LEVEL(quarter_secs) (NATIVE_FREQUENCY * (quarter_secs)) +#define DATA_LEVEL(quarter_secs) (pcmbuf_sampr * (quarter_secs)) /* Number of bytes played per second: (sample rate * 2 channels * 2 bytes/sample) */ -#define BYTERATE (NATIVE_FREQUENCY * 4) +#define BYTERATE (pcmbuf_sampr * 2 * 2) #if MEMORYSIZE > 2 /* Keep watermark high for large memory target - at least (2s) */ @@ -104,6 +103,7 @@ static size_t pcmbuf_size; static struct chunkdesc *pcmbuf_descriptors; static unsigned int pcmbuf_desc_count; static unsigned int position_key = 1; +static unsigned int pcmbuf_sampr = 0; static size_t chunk_ridx; static size_t chunk_widx; @@ -111,8 +111,7 @@ static size_t chunk_widx; static size_t pcmbuf_bytes_waiting; static struct chunkdesc *current_desc; -/* Only written if HAVE_CROSSFADE */ -static size_t pcmbuf_watermark = PCMBUF_WATERMARK; +static size_t pcmbuf_watermark = 0; static bool low_latency_mode = false; @@ -545,6 +544,8 @@ size_t pcmbuf_init(void *bufend) } pcmbuf_finish_crossfade_enable(); +#else + pcmbuf_watermark = PCMBUF_WATERMARK; #endif /* HAVE_CROSSFADE */ init_buffer_state(); @@ -1331,3 +1332,13 @@ void pcmbuf_set_low_latency(bool state) { low_latency_mode = state; } + +void pcmbuf_update_frequency(void) +{ + pcmbuf_sampr = mixer_get_frequency(); +} + +unsigned int pcmbuf_get_frequency(void) +{ + return pcmbuf_sampr; +} 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); /* Misc */ bool pcmbuf_is_lowdata(void); void pcmbuf_set_low_latency(bool state); +void pcmbuf_update_frequency(void); +unsigned int pcmbuf_get_frequency(void); #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) /* Must reset the buffer before use if trashed or voice only - voice file size shouldn't have changed so we can go straight from AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */ - if (buffer_state != AUDIOBUF_STATE_INITIALIZED) + if (buffer_state != AUDIOBUF_STATE_INITIALIZED || + !pcmbuf_is_same_size()) + { audio_reset_buffer(AUDIOBUF_STATE_INITIALIZED); + } logf("Starting buffer fill"); @@ -2510,6 +2513,11 @@ static void audio_start_playback(size_t offset, unsigned int flags) #ifndef PLATFORM_HAS_VOLUME_CHANGE sound_set_volume(global_settings.volume); #endif +#ifdef HAVE_PLAY_FREQ + settings_apply_play_freq(global_settings.play_frequency, true); +#endif + pcmbuf_update_frequency(); + /* Be sure channel is audible */ pcmbuf_fade(false, true); @@ -3755,6 +3763,7 @@ void INIT_ATTR playback_init(void) mutex_init(&id3_mutex); track_list_init(); buffering_init(); + pcmbuf_update_frequency(); add_event(PLAYBACK_EVENT_VOICE_PLAYING, false, playback_voice_event); #ifdef HAVE_CROSSFADE /* 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 = { /* new stuff at the end, sort into place next time the API gets incompatible */ + mixer_set_frequency, + mixer_get_frequency, }; int 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); #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 223 +#define PLUGIN_API_VERSION 224 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any @@ -970,6 +970,8 @@ struct plugin_api { /* new stuff at the end, sort into place next time the API gets incompatible */ + void (*mixer_set_frequency)(unsigned int samplerate); + unsigned int (*mixer_get_frequency)(void); }; /* 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 remote_control.c #endif +test_codec.c +test_sampr.c #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) init_mad(); td.dsp = rb->dsp_get_config(CODEC_IDX_AUDIO); + rb->dsp_configure(td.dsp, DSP_SET_OUT_FREQUENCY, CLOCK_RATE); #ifdef HAVE_PITCHCONTROL rb->sound_set_pitch(PITCH_SPEED_100); 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 @@ #define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE) /** PCM buffer **/ -#define CLOCK_RATE NATIVE_FREQUENCY /* Our clock rate in ticks/second (samplerate) */ +#define CLOCK_RATE 44100 /* Our clock rate in ticks/second (samplerate) */ /* Define this as "1" to have a test tone instead of silence clip */ #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 */ static int pcm_skipped = 0; static int pcm_underruns = 0; +static unsigned int old_sampr = 0; + /* Small silence clip. ~5.80ms @ 44.1kHz */ static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 }; @@ -380,9 +382,13 @@ bool pcm_output_init(void) } #endif + old_sampr = rb->mixer_get_frequency(); + rb->mixer_set_frequency(CLOCK_RATE); return true; } void pcm_output_exit(void) { + if (old_sampr != 0) + rb->mixer_set_frequency(old_sampr); } 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) /** Waveform View **/ #ifdef OSCILLOSCOPE_GRAPHMODE -static int16_t waveform_buffer[2*ALIGN_UP(NATIVE_FREQUENCY, 2048)+2*2048] +static int16_t waveform_buffer[2*ALIGN_UP(PLAY_SAMPR_MAX, 2048)+2*2048] MEM_ALIGN_ATTR; static size_t waveform_buffer_threshold = 0; static size_t volatile waveform_buffer_have = 0; static size_t waveform_buffer_break = 0; +static unsigned long mixer_sampr = PLAY_SAMPR_DEFAULT; #define PCM_SAMPLESIZE (2*sizeof(int16_t)) -#define PCM_BYTERATE (NATIVE_FREQUENCY*PCM_SAMPLESIZE) +#define PCM_BYTERATE(sampr) ((sampr)*PCM_SAMPLESIZE) #define WAVEFORM_SCALE_PCM(full_scale, sample) \ ((((full_scale) * (sample)) + (1 << 14)) >> 15) @@ -1390,7 +1391,7 @@ static long anim_waveform_horizontal(void) return cur_tick + HZ/5; } - int count = (NATIVE_FREQUENCY*osc_delay + 100*HZ - 1) / (100*HZ); + int count = (mixer_sampr*osc_delay + 100*HZ - 1) / (100*HZ); waveform_buffer_set_threshold(count*PCM_SAMPLESIZE); @@ -1516,7 +1517,8 @@ static long anim_waveform_horizontal(void) osd_lcd_update(); long delay = get_next_delay(); - return cur_tick + delay - waveform_buffer_have * HZ / PCM_BYTERATE; + return cur_tick + delay - waveform_buffer_have * HZ / + PCM_BYTERATE(mixer_sampr); } static void anim_waveform_plot_filled_v(int y, int y_prev, @@ -1583,7 +1585,7 @@ static long anim_waveform_vertical(void) return cur_tick + HZ/5; } - int count = (NATIVE_FREQUENCY*osc_delay + 100*HZ - 1) / (100*HZ); + int count = (mixer_sampr*osc_delay + 100*HZ - 1) / (100*HZ); waveform_buffer_set_threshold(count*PCM_SAMPLESIZE); @@ -1709,7 +1711,8 @@ static long anim_waveform_vertical(void) osd_lcd_update(); long delay = get_next_delay(); - return cur_tick + delay - waveform_buffer_have * HZ / PCM_BYTERATE; + return cur_tick + delay - waveform_buffer_have * HZ + / PCM_BYTERATE(mixer_sampr); } static void anim_waveform_exit(void) @@ -1872,6 +1875,10 @@ static void osc_setup(void) osd_lcd_update(); #endif +#ifdef OSCILLOSCOPE_GRAPHMODE + mixer_sampr = rb->mixer_get_frequency(); +#endif + /* Turn off backlight timeout */ backlight_ignore_timeout(); 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) { case DSP_SET_FREQUENCY: DEBUGF("samplerate=%d\n",(int)value); - wavinfo.samplerate = use_dsp ? NATIVE_FREQUENCY : (int)value; + if (use_dsp) { + wavinfo.samplerate = rb->dsp_configure( + ci.dsp, DSP_GET_OUT_FREQUENCY, 0); + } else { + wavinfo.samplerate = (int)value; + } break; 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) #endif +#define DSP_OUT_MIN_HZ PLAY_SAMPR_HW_MIN +#define DSP_OUT_MAX_HZ PLAY_SAMPR_MAX +#define DSP_OUT_DEFAULT_HZ PLAY_SAMPR_DEFAULT + #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; #ifdef HAVE_RECORDING #include "enc_config.h" #endif +#include "pcm_sampr.h" +#ifdef HAVE_PLAY_FREQ +#include "pcm_mixer.h" +#include "dsp_core.h" +#endif #endif /* CONFIG_CODEC == SWCODEC */ #define NVRAM_BLOCK_SIZE 44 @@ -720,6 +725,36 @@ void settings_apply_pm_range(void) } #endif /* HAVE_LCD_BITMAP */ +#ifdef HAVE_PLAY_FREQ +void settings_apply_play_freq(int value, bool playback) +{ + static const unsigned long play_sampr[] = { SAMPR_44, SAMPR_48 }; + static int prev_setting = 0; + + if ((unsigned)value >= ARRAYLEN(play_sampr)) + value = 0; + + bool changed = value != prev_setting; + prev_setting = value; + + long offset = 0; + bool playing = changed && !playback && + audio_status() == AUDIO_STATUS_PLAY; + + if (playing) + offset = audio_current_track()->offset; + + if (changed && !playback) + audio_hard_stop(); + + /* Other sub-areas of playback pick it up from the mixer */ + mixer_set_frequency(play_sampr[value]); + + if (playing) + audio_play(offset); +} +#endif /* HAVE_PLAY_FREQ */ + void sound_settings_apply(void) { #ifdef AUDIOHW_HAVE_BASS @@ -976,6 +1011,9 @@ void settings_apply(bool read_disk) set_codepage(global_settings.default_codepage); CHART(" +#include "config.h" #include "system.h" #include "core_alloc.h" #include "thread.h" @@ -30,8 +30,7 @@ #include "pcm_mixer.h" #include "codecs/libspeex/speex/speex.h" -/* Default number of native-frequency PCM frames to queue - adjust as - necessary per-target */ +/* Default number of PCM frames to queue - adjust as necessary per-target */ #define VOICE_FRAMES 4 /* 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; static int quiet_counter SHAREDDATA_ATTR = 0; static bool voice_playing = false; -#define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_COUNT + \ - VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE) +#define VOICE_PCM_FRAME_COUNT ((PLAY_SAMPR_MAX*VOICE_FRAME_COUNT + \ + VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE) #define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*2*sizeof (int16_t)) /* Voice processing states */ @@ -356,11 +355,13 @@ static enum voice_state voice_message(struct voice_thread_data *td) { /* Stop any clip still playing */ voice_stop_playback(); + dsp_configure(td->dsp, DSP_FLUSH, 0); } if (quiet_counter <= 0) { voice_playing = true; + dsp_configure(td->dsp, DSP_SET_OUT_FREQUENCY, mixer_get_frequency()); send_event(PLAYBACK_EVENT_VOICE_PLAYING, &voice_playing); } 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 @@ #endif #endif /* HAVE_RECORDING */ + +/* Samplerate config */ +#define PCM_SAMPR_CONFIG_ONLY /* no C code */ +#include "pcm_sampr.h" +#undef PCM_SAMPR_CONFIG_ONLY + +#define PLAY_SAMPR_CAPS (HW_SAMPR_CAPS & (SAMPR_CAP_44 | SAMPR_CAP_48)) +/** + * PLAY_SAMPR_MIN: The minimum allowable samplerate for global playback. + * Music won't play at a lower rate. + * PLAY_SAMPR_MAX: The maximum allowable samplerate for global playback. + * Music won't play at a faster rate. + * PLAY_SAMPR_DEFAULT: The default samplerate, unless configured otherwise. + * PLAY_SAMPR_HW_MIN: The minimum allowable rate for some subsystems such + * as the DSP core. DSP never exceeds *MAX to lessen + * buffer allocation demands and overhead. + */ +#if PLAY_SAMPR_CAPS & (PLAY_SAMPR_CAPS - 1) +#define HAVE_PLAY_FREQ +# define PLAY_SAMPR_MIN SAMPR_44 +# define PLAY_SAMPR_MAX SAMPR_48 +# define PLAY_SAMPR_DEFAULT SAMPR_44 +# define PLAY_SAMPR_HW_MIN HW_SAMPR_MIN +#elif PLAY_SAMPR_CAPS & SAMPR_CAP_44 +# define PLAY_SAMPR_MIN SAMPR_44 +# define PLAY_SAMPR_MAX SAMPR_44 +# define PLAY_SAMPR_DEFAULT SAMPR_44 +# define PLAY_SAMPR_HW_MIN HW_SAMPR_MIN +#elif PLAY_SAMPR_CAPS & SAMPR_CAP_48 +# define PLAY_SAMPR_MIN SAMPR_48 +# define PLAY_SAMPR_MAX SAMPR_48 +# define PLAY_SAMPR_DEFAULT SAMPR_48 +# define PLAY_SAMPR_HW_MIN HW_SAMPR_MIN +#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); #endif #endif /* CONFIG_SAMPR_TYPES */ +/* set next frequency to be used */ void pcm_set_frequency(unsigned int samplerate); +/* return last-set frequency */ +unsigned int pcm_get_frequency(void); /* apply settings to hardware immediately */ void pcm_apply_settings(void); 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, /* Stop ALL channels and PCM and reset state */ void mixer_reset(void); +/* Set output samplerate */ +void mixer_set_frequency(unsigned int samplerate); + +/* Get output samplerate */ +unsigned int mixer_get_frequency(void); + #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 @@ ****************************************************************************/ #ifndef PCM_SAMPR_H + +/* File might be included for CPP config macros only. Allow it to be included + * again for full C declarations. */ +#ifndef PCM_SAMPR_CONFIG_ONLY #define PCM_SAMPR_H +#endif #ifndef HW_SAMPR_CAPS #define HW_SAMPR_CAPS SAMPR_CAP_44 /* if not defined, default to 44100 */ @@ -75,11 +80,14 @@ SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +#ifndef PCM_SAMPR_CONFIG_ONLY /* Master list of all "standard" rates supported. */ extern const unsigned long audio_master_sampr_list[SAMPR_NUM_FREQ]; +#endif /* PCM_SAMPR_CONFIG_ONLY */ /** Hardware sample rates **/ +#ifndef PCM_SAMPR_CONFIG_ONLY /* Enumeration of supported frequencies where 0 is the highest rate supported and REC_NUM_FREQUENCIES is the number available */ enum hw_freq_indexes @@ -183,14 +191,49 @@ enum hw_freq_indexes #define HW_HAVE_8_(...) #endif HW_NUM_FREQ, - HW_FREQ_DEFAULT = HW_FREQ_44, - HW_SAMPR_DEFAULT = SAMPR_44, }; /* enum hw_freq_indexes */ /* list of hardware sample rates */ extern const unsigned long hw_freq_sampr[HW_NUM_FREQ]; +#endif /* PCM_SAMPR_CONFIG_ONLY */ + +#define HW_FREQ_DEFAULT HW_FREQ_44 +#define HW_SAMPR_DEFAULT SAMPR_44 + + +#if HW_SAMPR_CAPS & SAMPR_CAP_96 +# define HW_SAMPR_MAX SAMPR_96 +#elif HW_SAMPR_CAPS & SAMPR_CAP_88 +# define HW_SAMPR_MAX SAMPR_88 +#elif HW_SAMPR_CAPS & SAMPR_CAP_64 +# define HW_SAMPR_MAX SAMPR_64 +#elif HW_SAMPR_CAPS & SAMPR_CAP_48 +# define HW_SAMPR_MAX SAMPR_48 +#else +# define HW_SAMPR_MAX SAMPR_44 +#endif + +#if HW_SAMPR_CAPS & SAMPR_CAP_8 +# define HW_SAMPR_MIN SAMPR_8 +#elif HW_SAMPR_CAPS & SAMPR_CAP_11 +# define HW_SAMPR_MIN SAMPR_11 +#elif HW_SAMPR_CAPS & SAMPR_CAP_12 +# define HW_SAMPR_MIN SAMPR_12 +#elif HW_SAMPR_CAPS & SAMPR_CAP_16 +# define HW_SAMPR_MIN SAMPR_16 +#elif HW_SAMPR_CAPS & SAMPR_CAP_22 +# define HW_SAMPR_MIN SAMPR_22 +#elif HW_SAMPR_CAPS & SAMPR_CAP_24 +# define HW_SAMPR_MIN SAMPR_24 +#elif HW_SAMPR_CAPS & SAMPR_CAP_32 +# define HW_SAMPR_MIN SAMPR_32 +#else +# define HW_SAMPR_MIN SAMPR_44 +#endif #ifdef HAVE_RECORDING + +#ifndef PCM_SAMPR_CONFIG_ONLY /* Enumeration of supported frequencies where 0 is the highest rate supported and REC_NUM_FREQUENCIES is the number available */ enum rec_freq_indexes @@ -296,6 +339,10 @@ enum rec_freq_indexes REC_NUM_FREQ, }; /* enum rec_freq_indexes */ +/* List of recording supported sample rates (set or subset of master list) */ +extern const unsigned long rec_freq_sampr[REC_NUM_FREQ]; +#endif /* PCM_SAMPR_CONFIG_ONLY */ + /* Default to 44.1kHz if not otherwise specified */ #ifndef REC_FREQ_DEFAULT #define REC_FREQ_DEFAULT REC_FREQ_44 @@ -314,8 +361,7 @@ enum rec_freq_indexes REC_HAVE_16_(",16") REC_HAVE_12_(",12") \ REC_HAVE_11_(",11") REC_HAVE_8_(",8")[1] -/* List of recording supported sample rates (set or subset of master list) */ -extern const unsigned long rec_freq_sampr[REC_NUM_FREQ]; + #endif /* HAVE_RECORDING */ #ifdef CONFIG_SAMPR_TYPES @@ -326,8 +372,10 @@ extern const unsigned long rec_freq_sampr[REC_NUM_FREQ]; #define SAMPR_TYPE_REC (0x01 << 24) #endif +#ifndef PCM_SAMPR_CONFIG_ONLY unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate, unsigned int type); +#endif #else /* ndef CONFIG_SAMPR_TYPES */ 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) pcm_fsel = index; } +/* return last-set frequency */ +unsigned int pcm_get_frequency(void) +{ + return pcm_sampr; +} + /* apply pcm settings to the hardware */ void pcm_apply_settings(void) { 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 @@ #include "pcm.h" #include "pcm-internal.h" #include "pcm_mixer.h" -#include "dsp_core.h" /* For NATIVE_FREQUENCY */ /* Channels use standard-style PCM callback interface but a latency of one frame by double-buffering is introduced in order to facilitate mixing and @@ -33,6 +32,8 @@ before the last samples are sent to the codec and so things are done in parallel (as much as possible) with sending-out data. */ +static unsigned int mixer_sampr = HW_SAMPR_DEFAULT; + /* Define this to nonzero to add a marker pulse at each frame start */ #define FRAME_BOUNDARY_MARKERS 0 @@ -65,7 +66,7 @@ static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR; static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR; /* Number of silence frames to play after all data has played */ -#define MAX_IDLE_FRAMES (NATIVE_FREQUENCY*3 / MIX_FRAME_SAMPLES) +#define MAX_IDLE_FRAMES (mixer_sampr*3 / MIX_FRAME_SAMPLES) static unsigned int idle_counter = 0; /** Mixing routines, CPU optmized **/ @@ -256,7 +257,7 @@ static void mixer_start_pcm(void) #endif /* Requires a shared global sample rate for all channels */ - pcm_set_frequency(NATIVE_FREQUENCY); + pcm_set_frequency(mixer_sampr); /* Prepare initial frames and set up the double buffer */ mixer_buffer_callback(PCM_DMAST_STARTED); @@ -438,3 +439,23 @@ void mixer_reset(void) idle_counter = 0; } + +/* Set output samplerate */ +void mixer_set_frequency(unsigned int samplerate) +{ + pcm_set_frequency(samplerate); + samplerate = pcm_get_frequency(); + + if (samplerate == mixer_sampr) + return; + + /* All data is now invalid */ + mixer_reset(); + mixer_sampr = samplerate; +} + +/* Get output samplerate */ +unsigned int mixer_get_frequency(void) +{ + return mixer_sampr; +} 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 @@ #include "logf.h" #include "dsp_proc_entry.h" #include "compressor.h" +#include "dsp_misc.h" static struct compressor_settings curr_set; /* Cached settings */ @@ -40,7 +41,8 @@ static int32_t release_gain IBSS_ATTR; /* S7.24 format */ /** COMPRESSOR UPDATE * Called via the menu system to configure the compressor process */ -static bool compressor_update(const struct compressor_settings *settings) +static bool compressor_update(struct dsp_config *dsp, + const struct compressor_settings *settings) { /* make settings values useful */ int threshold = settings->threshold; @@ -48,9 +50,10 @@ static bool compressor_update(const struct compressor_settings *settings) static const int comp_ratios[] = { 2, 4, 6, 10, 0 }; int ratio = comp_ratios[settings->ratio]; bool soft_knee = settings->knee == 1; - int release = settings->release_time * NATIVE_FREQUENCY / 1000; + int release = settings->release_time * + dsp_get_output_frequency(dsp) / 1000; - bool changed = false; + bool changed = settings == &curr_set; /* If frequency change */ bool active = threshold < 0; if (memcmp(settings, &curr_set, sizeof (curr_set))) @@ -300,8 +303,8 @@ static inline int32_t get_compression_gain(struct sample_format *format, void dsp_set_compressor(const struct compressor_settings *settings) { /* enable/disable the compressor depending upon settings */ - bool enable = compressor_update(settings); struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); + bool enable = compressor_update(dsp, settings); dsp_proc_enable(dsp, DSP_PROC_COMPRESSOR, enable); dsp_proc_activate(dsp, DSP_PROC_COMPRESSOR, true); } @@ -386,15 +389,20 @@ static intptr_t compressor_configure(struct dsp_proc_entry *this, break; /* Already enabled */ this->process = compressor_process; + /* Won't have been getting frequency updates */ + compressor_update(dsp, &curr_set); /* Fall-through */ case DSP_RESET: case DSP_FLUSH: release_gain = UNITY; break; + + case DSP_SET_OUT_FREQUENCY: + compressor_update(dsp, &curr_set); + break; } return 0; - (void)dsp; } /* 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 @@ #include "fixedpoint.h" #include "fracmul.h" #include "replaygain.h" +#include "dsp_misc.h" #include "dsp_proc_entry.h" #include "dsp_filter.h" #include "crossfeed.h" @@ -44,32 +45,40 @@ void crossfeed_meier_process(struct dsp_proc_entry *this, * to listen to on headphones with no crossfeed. */ +#define DELAY_LEN(fs) ((300*(fs) / 1000000)*2) /* ~300 uS */ + /* Crossfeed */ static struct crossfeed_state { - int32_t gain; /* 00h: Direct path gain */ - int32_t coefs[3]; /* 04h: Coefficients for the shelving filter */ union { - struct /* 10h: Data for meier crossfeed */ + struct /* Data for meier crossfeed */ { - int32_t vcl; - int32_t vcr; - int32_t vdiff; - int32_t coef1; - int32_t coef2; + int32_t reserved; /* 00h: Reserved: overlaps gain */ + int32_t vcl; /* 04h: Left filter output */ + int32_t vcr; /* 08h: Right filter output */ + int32_t vdiff; /* 0ch: L-R difference signal */ + int32_t coef1; /* 10h: Left/right filter coef */ + int32_t coef2; /* 14h: Crossfeed filter coef */ }; - struct /* 10h: Data for custom crossfeed */ + struct /* Data for custom crossfeed */ { + int32_t gain; /* 00h: Direct path gain */ + int32_t coefs[3]; /* 04h: Filter coefficients: b0, b1, a1 */ int32_t history[4]; /* 10h: Format is x[n - 1], y[n - 1] (L + R) */ - int32_t delay[13*2];/* 20h: Delay line buffer (L + R interleaved) */ + int32_t *index; /* 20h: Current pointer into the delay line */ + int32_t *index_max; /* 24h: Current max pointer of delay line */ + /* 28h: Delay line buffer (L + R interleaved) */ + int32_t delay[DELAY_LEN(DSP_OUT_MAX_HZ)]; /* Target-dependent size */ }; }; - int32_t *index; /* 88h: Current pointer into the delay line */ - /* 8ch */ } crossfeed_state IBSS_ATTR; static int crossfeed_type = CROSSFEED_TYPE_NONE; +/* Cached custom settings */ +static long crossfeed_lf_gain; +static long crossfeed_hf_gain; +static long crossfeed_cutoff; /* Discard the sample histories */ static void crossfeed_flush(struct dsp_proc_entry *this) @@ -82,12 +91,49 @@ static void crossfeed_flush(struct dsp_proc_entry *this) } else { - memset(state->history, 0, - sizeof (state->history) + sizeof (state->delay)); + memset(state->history, 0, sizeof (state->history)); + memset(state->delay, 0, sizeof (state->delay)); state->index = state->delay; } } +static void crossfeed_meier_update_filter(struct crossfeed_state *state, + unsigned int fout) +{ + /* 1 / (F.Rforward.C) */ + state->coef1 = fp_div(2128, fout, 31); + /* 1 / (F.Rcross.C) */ + state->coef2 = fp_div(1000, fout, 31); +} + +static void crossfeed_custom_update_filter(struct crossfeed_state *state, + unsigned int fout) +{ + long lf_gain = crossfeed_lf_gain; + long hf_gain = crossfeed_hf_gain; + long cutoff = crossfeed_cutoff; + int32_t *c = state->coefs; + + long scaler = get_replaygain_int(lf_gain * 10) << 7; + + cutoff = fp_div(cutoff, fout, 32); + hf_gain -= lf_gain; + /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB + * point instead of shelf midpoint. This is for compatibility with the old + * crossfeed shelf filter and should be removed if crossfeed settings are + * ever made incompatible for any other good reason. + */ + cutoff = fp_div(cutoff, get_replaygain_int(hf_gain*5), 24); + + filter_shelf_coefs(cutoff, hf_gain, false, c); + /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains + * over 1 and can do this safely + */ + c[0] = FRACMUL_SHL(c[0], scaler, 4); + c[1] = FRACMUL_SHL(c[1], scaler, 4); + c[2] <<= 4; +} + /** DSP interface **/ @@ -114,24 +160,13 @@ void dsp_set_crossfeed_direct_gain(int gain) /* Both gains should be below 0 dB */ void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff) { - int32_t *c = crossfeed_state.coefs; - long scaler = get_replaygain_int(lf_gain * 10) << 7; + crossfeed_lf_gain = lf_gain; + crossfeed_hf_gain = hf_gain; + crossfeed_cutoff = cutoff; - cutoff = 0xffffffff / NATIVE_FREQUENCY * cutoff; - hf_gain -= lf_gain; - /* Divide cutoff by sqrt(10^(hf_gain/20)) to place cutoff at the -3 dB - * point instead of shelf midpoint. This is for compatibility with the old - * crossfeed shelf filter and should be removed if crossfeed settings are - * ever made incompatible for any other good reason. - */ - cutoff = fp_div(cutoff, get_replaygain_int(hf_gain*5), 24); - filter_shelf_coefs(cutoff, hf_gain, false, c); - /* Scale coefs by LF gain and shift them to s0.31 format. We have no gains - * over 1 and can do this safely - */ - c[0] = FRACMUL_SHL(c[0], scaler, 4); - c[1] = FRACMUL_SHL(c[1], scaler, 4); - c[2] <<= 4; + struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); + crossfeed_custom_update_filter(&crossfeed_state, + dsp_get_output_frequency(dsp)); } #if !defined(CPU_COLDFIRE) && !defined(CPU_ARM) @@ -147,6 +182,7 @@ void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p) int32_t *coefs = &state->coefs[0]; int32_t gain = state->gain; int32_t *di = state->index; + int32_t *di_max = state->index_max; int count = buf->remcount; @@ -176,7 +212,7 @@ void crossfeed_process(struct dsp_proc_entry *this, struct dsp_buffer **buf_p) buf->p32[1][i] = FRACMUL(right, gain) + hist_l[1]; /* Wrap delay line index if bigger than delay line size */ - if (di >= delay + 13*2) + if (di >= di_max) di = delay; } @@ -234,17 +270,21 @@ static void update_process_fn(struct dsp_proc_entry *this, struct dsp_config *dsp) { struct crossfeed_state *state = (struct crossfeed_state *)this->data; - dsp_proc_fn_type fn = crossfeed_process; + dsp_proc_fn_type fn; + + unsigned int fout = dsp_get_output_frequency(dsp); if (crossfeed_type != CROSSFEED_TYPE_CUSTOM) { - /* Set up for Meier */ - /* 1 / (F.Rforward.C) */ - state->coef1 = (0x7fffffff / NATIVE_FREQUENCY) * 2128; - /* 1 / (F.Rcross.C) */ - state->coef2 = (0x7fffffff / NATIVE_FREQUENCY) * 1000; + crossfeed_meier_update_filter(state, fout); fn = crossfeed_meier_process; } + else + { + state->index_max = state->delay + DELAY_LEN(fout); + crossfeed_custom_update_filter(state, fout); + fn = crossfeed_process; + } if (this->process != fn) { @@ -292,6 +332,7 @@ static intptr_t crossfeed_configure(struct dsp_proc_entry *this, if (value == 0) this->data = (intptr_t)&crossfeed_state; + case DSP_SET_OUT_FREQUENCY: update_process_fn(this, dsp); break; 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: @ to keep the count on the stack :/ ldr r1, [r1] @ r1 = buf = *buf_p; stmfd sp!, { r4-r11, lr } @ stack modified regs - ldr r12, [r1] @ r12 = buf->remcount - ldr r14, [r0] @ r14 = this->data = &crossfeed_state - ldmib r1, { r2-r3 } @ r2 = buf->p32[0], r3 = buf->p32[1] - ldmia r14!, { r4-r11 } @ load direct gain and filter data - add r0, r14, #13*2*4 @ calculate end of delay - stmfd sp!, { r0, r12 } @ stack end of delay adr, count and state - ldr r0, [r0] @ fetch current delay line address + ldr r0, [r0] @ r0 = this->data = &crossfeed_state + ldmia r1, { r1-r3 } @ r1 = buf->remcount, r2 = buf->p32[0], + @ r3 = buf->p32[1] + ldmia r0, { r4-r12, r14 } @ r4 = gain, r5-r7 = coeffs, + @ r8-r11 = history, r12 = index, + @ r14 = index_max + add r0, r0, #0x28 @ r0 = state->delay + stmfd sp!, { r0-r1, r14 } @ stack state->delay, count, index_max - /* Register usage in loop: - * r0 = &delay[index][0], r1 = accumulator high, r2 = buf->p32[0], + /* Register usage in loop: + * r0 = acc low/count, r1 = acc high, r2 = buf->p32[0], * r3 = buf->p32[1], r4 = direct gain, r5-r7 = b0, b1, a1 (filter coefs), - * r8-r11 = filter history, r12 = temp, r14 = accumulator low + * r8 = dr[n-1], r9 = y_r[n-1], r10 = dl[n-1], r11 = y_l[n-1], + * r12 = index, r14 = scratch/index_max */ .cfloop: - smull r14, r1, r6, r8 @ acc = b1*dr[n - 1] - smlal r14, r1, r7, r9 @ acc += a1*y_l[n - 1] - ldr r8, [r0, #4] @ r8 = dr[n] - smlal r14, r1, r5, r8 @ acc += b0*dr[n] - mov r9, r1, lsl #1 @ fix format for filter history - ldr r12, [r2] @ load left input - smlal r14, r1, r4, r12 @ acc += gain*x_l[n] - mov r1, r1, lsl #1 @ fix format + smull r0, r1, r6, r8 @ acc = b1*dr[n - 1] + ldr r8, [r12, #4] @ r8 = dr[n] + smlal r0, r1, r7, r9 @ acc += a1*y_r[n - 1] + smlal r0, r1, r5, r8 @ acc += b0*dr[n] + ldr r14, [r2] @ load left input: x_l[n] + mov r9, r1, asl #1 @ fix format for filter history + smlal r0, r1, r4, r14 @ acc += gain*x_l[n] + mov r1, r1, asl #1 @ fix format str r1, [r2], #4 @ save result - - smull r14, r1, r6, r10 @ acc = b1*dl[n - 1] - smlal r14, r1, r7, r11 @ acc += a1*y_r[n - 1] - ldr r10, [r0] @ r10 = dl[n] - str r12, [r0], #4 @ save left input to delay line - smlal r14, r1, r5, r10 @ acc += b0*dl[n] - mov r11, r1, lsl #1 @ fix format for filter history - ldr r12, [r3] @ load right input - smlal r14, r1, r4, r12 @ acc += gain*x_r[n] - str r12, [r0], #4 @ save right input to delay line - mov r1, r1, lsl #1 @ fix format - ldmia sp, { r12, r14 } @ fetch delay line end addr and count from stack + smull r0, r1, r6, r10 @ acc = b1*dl[n - 1] + ldr r10, [r12] @ r10 = dl[n] + smlal r0, r1, r7, r11 @ acc += a1*y_l[n - 1] + smlal r0, r1, r5, r10 @ acc += b0*dl[n] + str r14, [r12], #4 @ save left input to delay line + ldr r14, [r3] @ load right input: x_r[n] + mov r11, r1, asl #1 @ fix format for filter history + smlal r0, r1, r4, r14 @ acc += gain*x_r[n] + str r14, [r12], #4 @ save right input to delay line + ldmib sp, { r0, r14 } @ fetch count and delay end + mov r1, r1, asl #1 @ fix format str r1, [r3], #4 @ save result - cmp r0, r12 @ need to wrap to start of delay? - subhs r0, r12, #13*2*4 @ wrap back delay line ptr to start + cmp r12, r14 @ need to wrap to start of delay? + ldrhs r12, [sp] @ wrap delay index - subs r14, r14, #1 @ are we finished? - strgt r14, [sp, #4] @ nope, save count back to stack + subs r0, r0, #1 @ are we finished? + strgt r0, [sp, #4] @ save count to stack bgt .cfloop @ save data back to struct - str r0, [r12] @ save delay line index - sub r12, r12, #13*2*4 + 4*4 @ r12 = data->history - stmia r12, { r8-r11 } @ save filter history - add sp, sp, #8 @ remove temp variables from stack + ldr r0, [sp] @ fetch state->delay + sub r0, r0, #0x18 @ save filter history and delay index + stmia r0, { r8-r12 } @ + add sp, sp, #12 @ remove temp variables from stack ldmpc regs=r4-r11 .size crossfeed_process, .-crossfeed_process @@ -260,8 +261,7 @@ crossfeed_meier_process: ldr r0, [r0] @ r0 = this->data = &crossfeed_state stmfd sp!, { r4-r10, lr } @ stack non-volatile context ldmia r1, { r1-r3 } @ r1 = buf->remcout, r2=p32[0], r3=p32[1] - add r0, r0, #16 @ r0 = &state->vcl - ldmia r0, { r4-r8 } @ r4 = vcl, r5 = vcr, r6 = vdiff + ldmib r0, { r4-r8 } @ r4 = vcl, r5 = vcr, r6 = vdiff @ r7 = coef1, r8 = coef2 .cfm_loop: ldr r12, [r2] @ r12 = lout @@ -285,7 +285,7 @@ crossfeed_meier_process: sub r5, r5, r12 @ r5 = vcr -= res2 bgt .cfm_loop @ more samples? - stmia r0, { r4-r6 } @ save vcl, vcr, vdiff + stmib r0, { r4-r6 } @ save vcl, vcr, vdiff ldmpc regs=r4-r10 @ restore non-volatile context, return .size crossfeed_meier_process, .-crossfeed_meier_process 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: movem.l %d2-%d7/%a2-%a6, (%sp) | save all regs movem.l 48(%sp), %a1/%a4 | %a1 = this, %a4 = buf_p move.l (%a4), %a4 | %a4 = buf = *buf_p - movem.l (%a4), %d7/%a4-%a5 | %d7 = buf->remcount, %a4 = buf->p32[0], + movem.l (%a4), %d0/%a4-%a5 | %d0 = buf->remcount, %a4 = buf->p32[0], | %a5 = buf->p32[1] - move.l (%a1), %a1 | %a1 = &crossfeed_state - move.l (%a1)+, %d6 | %d6 = direct gain - movem.l 12(%a1), %d0-%d3 | fetch filter history samples - lea.l 132(%a1), %a6 | %a6 = delay line wrap limit - move.l (%a6), %a0 | fetch delay line address - movem.l (%a1), %a1-%a3 | load filter coefs - bra.b 20f | loop start | go to loop start point + move.l (%a1), %a6 | %d7 = state = &crossfeed_state + movem.l (%a6), %d1-%d6/%a0-%a3 | %d1 = gain, %d2-%d4 = coefs, + | %d5..%d6 = history[0..1], + | %a0..%a1 = history[2..3], + | %a2 = index, %a3 = index_max + lea.l 0x28(%a6), %a6 | %a6 = state->delay + move.l %a6, -(%sp) | push state->delay + bra.b .cfp_loop_start /* Register usage in loop: - * %a0 = delay_p, %a1..%a3 = b0, b1, a1 (filter coefs), - * %a4 = buf[0], %a5 = buf[1], - * %a6 = delay line pointer wrap limit, - * %d0..%d3 = history - * %d4..%d5 = temp. - * %d6 = direct gain, - * %d7 = count + * %d0 = count, %d1 = direct gain, %d2..%d4 = b0, b1, a1 (filter coefs), + * %d5..%d6 = history[0..1], %d7 = scratch + * %a0..%a1 = history[2..3], %a2 = index, %a3 = index_max, + * %a4 = buf[0], %a5 = buf[1], %a6 = scratch */ -10: | loop | - movclr.l %acc0, %d4 | write outputs - move.l %d4, (%a4)+ | . - movclr.l %acc1, %d5 | . - move.l %d5, (%a5)+ | . -20: | loop start | - mac.l %a2, %d0, (%a0)+, %d0, %acc0 | %acc0 = b1*dl[n - 1], %d0 = dl[n] - mac.l %a1, %d0 , %acc0 | %acc0 += b0*dl[n] - mac.l %a3, %d1, (%a5), %d5, %acc0 | %acc0 += a1*y_r[n - 1], load R - mac.l %a2, %d2, (%a0)+, %d2, %acc1 | %acc1 = b1*dr[n - 1], %d2 = dr[n] - mac.l %a1, %d2 , %acc1 | %acc1 += b0*dr[n] - mac.l %a3, %d3, (%a4), %d4, %acc1 | %acc1 += a1*y_l[n - 1], load L - movem.l %d4-%d5, -8(%a0) | save left & right inputs to delay line - move.l %acc0, %d3 | get filtered delayed left sample (y_l[n]) - move.l %acc1, %d1 | get filtered delayed right sample (y_r[n]) - mac.l %d6, %d4, %acc0 | %acc0 += gain*x_l[n] - mac.l %d6, %d5, %acc1 | %acc1 += gain*x_r[n] - cmp.l %a6, %a0 | wrap %a0 if passed end - bhs.b 30f | wrap buffer | - tpf.l | trap the buffer wrap -30: | wrap buffer | ...fwd taken branches more costly - lea.l -104(%a6), %a0 | wrap it up - subq.l #1, %d7 | --count > 0 ? - bgt.b 10b | loop | yes? do more - movclr.l %acc0, %d4 | write last outputs - move.l %d4, (%a4) | . - movclr.l %acc1, %d5 | . - move.l %d5, (%a5) | . - movem.l %d0-%d3, -120(%a6) | ...history - move.l %a0, (%a6) | ...delay_p +.cfp_loop: + movclr.l %acc0, %d7 | write outputs + move.l %d7, (%a4)+ | . + movclr.l %acc1, %a6 | . + move.l %a6, (%a5)+ | . +.cfp_loop_start: + mac.l %d3, %d5, (%a2)+, %d5, %acc1 | %acc1 = b1*dl[n - 1], %d5 = dl[n] + mac.l %d2, %d5 , %acc1 | %acc1 += b0*dl[n] + mac.l %d4, %d6, (%a4), %d7, %acc1 | %acc1 += a1*y_l[n - 1], %d7 = x_l[n] + mac.l %d3, %a0, (%a2)+, %a0, %acc0 | %acc0 = b1*dr[n - 1], %a0 = dr[n] + mac.l %a2, %a0 , %acc0 | %acc0 += b0*dr[n] + mac.l %d4, %a1, (%a5), %a6, %acc0 | %acc0 += a1*y_r[n - 1], %a6 = x_r[n] + movem.l %d7/%a6, -8(%a2) | save x_l[n] and x_r[n] to delay line + move.l %acc1, %d6 | get filtered delayed left sample (y_l[n]) + move.l %acc0, %a1 | get filtered delayed right sample (y_r[n]) + mac.l %d1, %d7, %acc0 | %acc0 = gain*x_l[n] + y_r[n] + mac.l %d1, %a6, %acc1 | %acc1 = gain*x_r[n] + y_l[n] + + cmp.l %a3, %a2 | wrap index if past end + bhs.b 1f | + tpf.w | trap the buffer wrap +1: | ...fwd taken branches more costly + move.l (%sp), %a2 | 2b | wrap it up + + subq.l #1, %d0 | --count > 0 ? + bgt.b .cfp_loop | yes? do more + + movclr.l %acc0, %d7 | write last outputs + move.l %d7, (%a4) | . + movclr.l %acc1, %a6 | . + move.l %a6, (%a5) | . + + move.l (%sp)+, %a6 | pop state->delay + movem.l %d5-%d6/%a0-%a2, -0x18(%a6) | save history, index movem.l (%sp), %d2-%d7/%a2-%a6 | restore all regs lea.l 44(%sp), %sp | rts | - .size crossfeed_process,.-crossfeed_process + .size crossfeed_process, .-crossfeed_process /**************************************************************************** * void crossfeed_meier_process(struct dsp_proc_entry *this, @@ -147,7 +149,7 @@ crossfeed_meier_process: movem.l %d2-%d6/%a2, (%sp) | . move.l (%a0), %a0 | %a0 = &this->data = &crossfeed_state move.l (%a1), %a1 | %a1 = buf = *buf_p - movem.l 16(%a0), %d1-%d5 | %d1 = vcl, %d2 = vcr, %d3 = vdiff, + movem.l 4(%a0), %d1-%d5 | %d1 = vcl, %d2 = vcr, %d3 = vdiff, | %d4 = coef1, %d5 = coef2 movem.l (%a1), %d0/%a1-%a2 | %d0 = count = buf->remcount | %a1 = p32[0], %a2 = p32[1] @@ -155,7 +157,7 @@ crossfeed_meier_process: | %d0 = count, %d1 = vcl, %d2 = vcr, %d3 = vdiff/lout, | %d4 = coef1, %d5 = coef2, %d6 = rout/scratch | %a1 = p32[0], %a2 = p32[1] -10: | loop +.cfmp_loop: mac.l %d5, %d3, %acc0 | %acc0 = common = coef2*vdiff move.l %acc0, %acc1 | copy common mac.l %d4, %d1, (%a1), %d3, %acc0 | %acc0 += coef1*vcl, %d3 = lout @@ -170,9 +172,9 @@ crossfeed_meier_process: movclr.l %acc1, %d6 | %d5 = fetch -res2 in s0.31 add.l %d6, %d2 | vcr += -res2 subq.l #1, %d0 | count-- - bgt 10b | loop | more samples? + bgt .cfmp_loop | more samples? | - movem.l %d1-%d3, 16(%a0) | save vcl, vcr, vdiff + movem.l %d1-%d3, 4(%a0) | save vcl, vcr, vdiff movem.l (%sp), %d2-%d6/%a2 | restore non-volatiles lea.l 24(%sp), %sp | . 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, intptr_t value) { bool multi = setting < DSP_PROC_SETTING; - struct dsp_proc_slot *s = multi ? dsp->proc_slots : - find_proc_slot(dsp, setting - DSP_PROC_SETTING); + struct dsp_proc_slot *s; + + if (multi) + { + /* Message to all enabled stages */ + if (dsp_sample_io_configure(&dsp->io_data, setting, &value)) + return value; /* To I/O only */ + + s = dsp->proc_slots; + } + else + { + /* Message to a particular stage */ + s = find_proc_slot(dsp, setting - DSP_PROC_SETTING); + } while (s != NULL) { @@ -117,7 +130,7 @@ static intptr_t proc_broadcast(struct dsp_config *dsp, unsigned int setting, s = s->next; } - return multi ? 1 : 0; + return 0; } /* Add an item to the enabled list */ @@ -244,6 +257,12 @@ void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id, proc_db_entry(s)->configure(&s->proc_entry, dsp, DSP_PROC_CLOSE, 0); } +/* Is the stage specified by the id currently enabled? */ +bool dsp_proc_enabled(struct dsp_config *dsp, enum dsp_proc_ids id) +{ + return (dsp->proc_mask_enabled & BIT_N(id)) != 0; +} + /* Activate or deactivate a stage */ void dsp_proc_activate(struct dsp_config *dsp, enum dsp_proc_ids id, bool activate) @@ -454,7 +473,6 @@ void dsp_process(struct dsp_config *dsp, struct dsp_buffer *src, intptr_t dsp_configure(struct dsp_config *dsp, unsigned int setting, intptr_t value) { - dsp_sample_io_configure(&dsp->io_data, setting, value); return proc_broadcast(dsp, setting, value); } @@ -497,7 +515,8 @@ void INIT_ATTR dsp_init(void) count = slot_count[i]; dsp->slot_free_mask = MASK_N(uint32_t, count, shift); - dsp_sample_io_configure(&dsp->io_data, DSP_INIT, i); + intptr_t value = i; + dsp_sample_io_configure(&dsp->io_data, DSP_INIT, &value); /* Notify each db entry of global init for each DSP */ 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 DSP_SET_STEREO_MODE, DSP_FLUSH, DSP_SET_PITCH, + DSP_SET_OUT_FREQUENCY, + DSP_GET_OUT_FREQUENCY, DSP_PROC_INIT, DSP_PROC_CLOSE, DSP_PROC_NEW_FORMAT, DSP_PROC_SETTING, /* stage-specific should be this + id */ }; -#define NATIVE_FREQUENCY 44100 /* internal/output sample rate */ - enum dsp_stereo_modes { 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) } #endif /* HAVE_PITCHCONTROL */ +/* Set output samplerate for all DSPs */ +void dsp_set_all_output_frequency(unsigned int samplerate) +{ + + struct dsp_config *dsp; + for (int i = 0; (dsp = dsp_get_config(i)); i++) + dsp_configure(dsp, DSP_SET_OUT_FREQUENCY, samplerate); +} + +/* Return DSP's output samplerate */ +unsigned int dsp_get_output_frequency(struct dsp_config *dsp) +{ + return dsp_configure(dsp, DSP_GET_OUT_FREQUENCY, 0); +} + static void INIT_ATTR misc_dsp_init(struct dsp_config *dsp, enum dsp_ids dsp_id) { 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); int32_t dsp_get_pitch(void); #endif /* HAVE_PITCHCONTROL */ +/* Set output samplerate for all DSPs */ +void dsp_set_all_output_frequency(unsigned int samplerate); + +/* Return DSP's output samplerate */ +struct dsp_config; +unsigned int dsp_get_output_frequency(struct dsp_config *dsp); + #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 #ifdef HAVE_PITCHCONTROL DSP_PROC_DB_ITEM(TIMESTRETCH) /* time-stretching */ #endif - DSP_PROC_DB_ITEM(RESAMPLE) /* resampler providing NATIVE_FREQUENCY */ + DSP_PROC_DB_ITEM(RESAMPLE) /* resampler providing output frequency */ DSP_PROC_DB_ITEM(CROSSFEED) /* stereo crossfeed */ DSP_PROC_DB_ITEM(EQUALIZER) /* n-band equalizer */ #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, * by processing code! */ void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id, bool enable); + +/* Is the specified stage enabled on the DSP? */ +bool dsp_proc_enabled(struct dsp_config *dsp, enum dsp_proc_ids id); + /* Activate/deactivate processing stage, doesn't affect enabled status * thus will not enable anything - * 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, static void INIT_ATTR dsp_sample_io_init(struct sample_io_data *this, enum dsp_ids dsp_id) { + this->output_sampr = DSP_OUT_DEFAULT_HZ; dsp_sample_input_init(this, dsp_id); dsp_sample_output_init(this); } -void dsp_sample_io_configure(struct sample_io_data *this, +bool dsp_sample_io_configure(struct sample_io_data *this, unsigned int setting, - intptr_t value) + intptr_t *value_p) { + intptr_t value = *value_p; + switch (setting) { case DSP_INIT: @@ -306,15 +309,15 @@ void dsp_sample_io_configure(struct sample_io_data *this, this->format.num_channels = 2; this->format.frac_bits = WORD_FRACBITS; this->format.output_scale = WORD_FRACBITS + 1 - NATIVE_DEPTH; - this->format.frequency = NATIVE_FREQUENCY; - this->format.codec_frequency = NATIVE_FREQUENCY; + this->format.frequency = this->output_sampr; + this->format.codec_frequency = this->output_sampr; this->sample_depth = NATIVE_DEPTH; this->stereo_mode = STEREO_NONINTERLEAVED; break; case DSP_SET_FREQUENCY: format_change_set(this); - value = value > 0 ? value : NATIVE_FREQUENCY; + value = value > 0 ? (unsigned int)value : this->output_sampr; this->format.frequency = value; this->format.codec_frequency = value; break; @@ -345,5 +348,22 @@ void dsp_sample_io_configure(struct sample_io_data *this, this->format.frequency = fp_mul(value, this->format.codec_frequency, 16); break; + + case DSP_SET_OUT_FREQUENCY: + value = value > 0 ? value : DSP_OUT_DEFAULT_HZ; + value = MIN(DSP_OUT_MAX_HZ, MAX(DSP_OUT_MIN_HZ, value)); + *value_p = value; + + if ((unsigned int)value == this->output_sampr) + return true; /* No change; don't broadcast */ + + this->output_sampr = value; + break; + + case DSP_GET_OUT_FREQUENCY: + *value_p = this->output_sampr; + return true; /* Only I/O handles it */ } + + return false; } 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 struct dsp_buffer sample_buf; /* Buffer descriptor for converted samples */ int32_t *sample_buf_p[2]; /* Internal format buffer pointers */ sample_output_fn_type output_samples; /* Final output function */ + unsigned int output_sampr; /* Master output samplerate */ uint8_t format_dirty; /* Format change set, avoids superfluous increments before carrying it out */ 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, struct sample_format *format); /* Sample IO watches the format setting from the codec */ -void dsp_sample_io_configure(struct sample_io_data *this, +bool dsp_sample_io_configure(struct sample_io_data *this, unsigned int setting, - intptr_t value); + intptr_t *value_p); #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 @@ #include "dsp_filter.h" #include "dsp_proc_entry.h" #include "dsp_core.h" +#include "dsp_misc.h" #include "eq.h" #include "pga.h" #include "replaygain.h" @@ -42,6 +43,9 @@ #error Band count must be greater than or equal to 3 #endif +/* Cached band settings */ +static struct eq_band_setting settings[EQ_NUM_BANDS]; + static struct eq_state { uint32_t enabled; /* Mask of enabled bands */ @@ -49,16 +53,48 @@ static struct eq_state struct dsp_filter filters[EQ_NUM_BANDS]; /* Data for each filter */ } eq_data IBSS_ATTR; +#define FOR_EACH_ENB_BAND(b) \ + for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++) + /* Clear histories of all enabled bands */ static void eq_flush(void) { if (eq_data.enabled == 0) return; /* Not initialized yet/no bands on */ - for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++) + FOR_EACH_ENB_BAND(b) filter_flush(&eq_data.filters[*b]); } +static void update_band_filter(int band, unsigned int fout) +{ + /* Convert user settings to format required by coef generator + functions */ + typeof (filter_pk_coefs) *coef_gen = filter_pk_coefs; + + /* Only first and last bands are not peaking filters */ + if (band == 0) + coef_gen = filter_ls_coefs; + else if (band == EQ_NUM_BANDS-1) + coef_gen = filter_hs_coefs; + + const struct eq_band_setting *setting = &settings[band]; + struct dsp_filter *filter = &eq_data.filters[band]; + + coef_gen(fp_div(setting->cutoff, fout, 32), setting->q ?: 1, + setting->gain, filter); +} + +/* Resync all bands to a new DSP output frequency */ +static void update_samplerate(unsigned int fout) +{ + if (eq_data.enabled == 0) + return; /* Not initialized yet/no bands on */ + + FOR_EACH_ENB_BAND(b) + update_band_filter(*b, fout); +} + /** DSP interface **/ /* Set the precut gain value */ @@ -73,11 +109,14 @@ void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting) if (band < 0 || band >= EQ_NUM_BANDS) return; + settings[band] = *setting; /* cache setting */ + + struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); + /* NOTE: The coef functions assume the EMAC unit is in fractional mode, which it should be, since we're executed from the main thread. */ uint32_t mask = eq_data.enabled; - struct dsp_filter *filter = &eq_data.filters[band]; /* Assume a band is disabled if the gain is zero */ mask &= ~BIT_N(band); @@ -85,33 +124,19 @@ void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting) if (setting->gain != 0) { mask |= BIT_N(band); - - /* Convert user settings to format required by coef generator - functions */ - void (* coef_gen)(unsigned long cutoff, unsigned long Q, long db, - struct dsp_filter *f) = filter_pk_coefs; - - /* Only first and last bands are not peaking filters */ - if (band == 0) - coef_gen = filter_ls_coefs; - else if (band == EQ_NUM_BANDS-1) - coef_gen = filter_hs_coefs; - - coef_gen(0xffffffff / NATIVE_FREQUENCY * setting->cutoff, - setting->q ?: 1, setting->gain, filter); + update_band_filter(band, dsp_get_output_frequency(dsp)); } if (mask == eq_data.enabled) return; /* No change in band-enable state */ if (mask & BIT_N(band)) - filter_flush(filter); /* Coming online */ + filter_flush(&eq_data.filters[band]); /* Coming online */ eq_data.enabled = mask; /* Only be active if there are bands to process - if EQ is off, then this call has no effect */ - struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); dsp_proc_activate(dsp, DSP_PROC_EQUALIZER, mask != 0); /* Prepare list of enabled bands for efficient iteration */ @@ -125,6 +150,11 @@ void dsp_set_eq_coefs(int band, const struct eq_band_setting *setting) void dsp_eq_enable(bool enable) { struct dsp_config *dsp = dsp_get_config(CODEC_IDX_AUDIO); + bool enabled = dsp_proc_enabled(dsp, DSP_PROC_EQUALIZER); + + if (enable == enabled) + return; + dsp_proc_enable(dsp, DSP_PROC_EQUALIZER, enable); if (enable && eq_data.enabled != 0) @@ -139,7 +169,7 @@ static void eq_process(struct dsp_proc_entry *this, int count = buf->remcount; unsigned int channels = buf->format.num_channels; - for (uint8_t *b = eq_data.bands; *b < EQ_NUM_BANDS; b++) + FOR_EACH_ENB_BAND(b) filter_process(&eq_data.filters[*b], buf->p32, count, channels); (void)this; @@ -154,10 +184,9 @@ static intptr_t eq_configure(struct dsp_proc_entry *this, switch (setting) { case DSP_PROC_INIT: - if (value != 0) - break; /* Already enabled */ - this->process = eq_process; + /* Wouldn't have been getting frequency updates */ + update_samplerate(dsp_get_output_frequency(dsp)); /* Fall-through */ case DSP_PROC_CLOSE: pga_enable_gain(PGA_EQ_PRECUT, setting == DSP_PROC_INIT); @@ -166,6 +195,10 @@ static intptr_t eq_configure(struct dsp_proc_entry *this, case DSP_FLUSH: eq_flush(); break; + + case DSP_SET_OUT_FREQUENCY: + update_samplerate(value); + break; } 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 @@ #include "fracmul.h" #include "fixedpoint.h" #include "dsp_proc_entry.h" +#include "dsp_misc.h" #include /** @@ -50,9 +51,10 @@ static struct resample_data int32_t history[2][3]; /* 08h: Last samples for interpolation (L+R) 0 = oldest, 2 = newest */ /* 20h */ - int32_t frequency; /* Virtual samplerate */ + unsigned int frequency; /* Virtual input samplerate */ + unsigned int frequency_out; /* Resampler output samplerate */ struct dsp_buffer resample_buf; /* Buffer descriptor for resampled data */ - int32_t *resample_out_p[2]; /* Actual output buffer pointers */ + int32_t *resample_out_p[2]; /* Actual output buffer pointers */ } resample_data[DSP_COUNT] IBSS_ATTR; /* Actual worker function. Implemented here or in target assembly code. */ @@ -73,14 +75,16 @@ static void resample_flush(struct dsp_proc_entry *this) } static bool resample_new_delta(struct resample_data *data, - struct sample_format *format) + struct sample_format *format, + unsigned int fout) { - int32_t frequency = format->frequency; /* virtual samplerate */ + unsigned int frequency = format->frequency; /* virtual samplerate */ data->frequency = frequency; - data->delta = fp_div(frequency, NATIVE_FREQUENCY, 16); + data->frequency_out = fout; + data->delta = fp_div(frequency, fout, 16); - if (frequency == NATIVE_FREQUENCY) + if (frequency == data->frequency_out) { /* NOTE: If fully glitch-free transistions from no resampling to resampling are desired, history should be maintained even when @@ -232,20 +236,23 @@ static intptr_t resample_new_format(struct dsp_proc_entry *this, DSP_PRINT_FORMAT(DSP_PROC_RESAMPLE, *format); - int32_t frequency = data->frequency; + unsigned int frequency = data->frequency; + unsigned int fout = dsp_get_output_frequency(dsp); bool active = dsp_proc_active(dsp, DSP_PROC_RESAMPLE); - if (format->frequency != frequency) + if ((unsigned int)format->frequency != frequency || + data->frequency_out != fout) { - DEBUGF(" DSP_PROC_RESAMPLE- new delta\n"); - active = resample_new_delta(data, format); + DEBUGF(" DSP_PROC_RESAMPLE- new settings: %u %u\n", + format->frequency, fout); + active = resample_new_delta(data, format, fout); dsp_proc_activate(dsp, DSP_PROC_RESAMPLE, active); } - /* Everything after us is NATIVE_FREQUENCY */ + /* Everything after us is fout */ dst->format = *format; - dst->format.frequency = NATIVE_FREQUENCY; - dst->format.codec_frequency = NATIVE_FREQUENCY; + dst->format.frequency = fout; + dst->format.codec_frequency = fout; if (active) return PROC_NEW_FORMAT_OK; @@ -287,8 +294,10 @@ static void INIT_ATTR resample_dsp_init(struct dsp_config *dsp, static void INIT_ATTR resample_proc_init(struct dsp_proc_entry *this, struct dsp_config *dsp) { + struct resample_data *data = &resample_data[dsp_get_id(dsp)]; + this->data = (intptr_t)data; dsp_proc_set_in_place(dsp, DSP_PROC_RESAMPLE, false); - this->data = (intptr_t)&resample_data[dsp_get_id(dsp)]; + data->frequency_out = DSP_OUT_DEFAULT_HZ; this->process = resample_process; } @@ -322,6 +331,10 @@ static intptr_t resample_configure(struct dsp_proc_entry *this, case DSP_PROC_NEW_FORMAT: retval = resample_new_format(this, dsp, (struct sample_format *)value); break; + + case DSP_SET_OUT_FREQUENCY: + dsp_proc_want_format_update(dsp, DSP_PROC_RESAMPLE); + break; } 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 @@ ****************************************************************************/ #include "rbcodecconfig.h" #include "platform.h" +#include "fixedpoint.h" #include "dsp_proc_entry.h" #include "dsp_filter.h" #include "tone_controls.h" +#include "dsp_misc.h" /* These apply to all DSP streams to remain as consistant as possible with * the behavior of hardware tone controls */ @@ -36,32 +38,39 @@ static const unsigned int tone_treble_cutoff = 3500; static int tone_bass = 0; static int tone_treble = 0; +/* Current prescaler setting */ +static int tone_prescale = 0; + /* Data for each DSP */ static struct dsp_filter tone_filters[DSP_COUNT] IBSS_ATTR; +static void update_filter(int id, unsigned int fout) +{ + filter_bishelf_coefs(fp_div(tone_bass_cutoff, fout, 32), + fp_div(tone_treble_cutoff, fout, 32), + tone_bass, tone_treble, -tone_prescale, + &tone_filters[id]); +} + /* Update the filters' coefficients based upon the bass/treble settings */ void tone_set_prescale(int prescale) { int bass = tone_bass; int treble = tone_treble; - struct dsp_filter tone_filter; /* Temp to hold new version */ - filter_bishelf_coefs(0xffffffff / NATIVE_FREQUENCY * tone_bass_cutoff, - 0xffffffff / NATIVE_FREQUENCY * tone_treble_cutoff, - bass, treble, -prescale, &tone_filter); + tone_prescale = prescale; struct dsp_config *dsp; for (int i = 0; (dsp = dsp_get_config(i)); i++) { - struct dsp_filter *filter = &tone_filters[i]; - filter_copy(filter, &tone_filter); + update_filter(i, dsp_get_output_frequency(dsp)); bool enable = bass != 0 || treble != 0; dsp_proc_enable(dsp, DSP_PROC_TONE_CONTROLS, enable); - if (!dsp_proc_active(dsp, DSP_PROC_TONE_CONTROLS)) + if (enable && !dsp_proc_active(dsp, DSP_PROC_TONE_CONTROLS)) { - filter_flush(filter); /* Going online */ + filter_flush(&tone_filters[i]); /* Going online */ dsp_proc_activate(dsp, DSP_PROC_TONE_CONTROLS, true); } } @@ -109,6 +118,10 @@ static intptr_t tone_configure(struct dsp_proc_entry *this, case DSP_FLUSH: filter_flush((struct dsp_filter *)this->data); break; + + case DSP_SET_OUT_FREQUENCY: + update_filter(dsp_get_id(dsp), value); + break; } 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 @@ #define HAVE_SW_TONE_CONTROLS #define HAVE_ALBUMART #define NUM_CORES 1 +/* All the same unless a configuration option is added to warble */ +#define DSP_OUT_MIN_HZ 44100 +#define DSP_OUT_DEFAULT_HZ 44100 +#define DSP_OUT_MAX_HZ 44100 #ifndef __ASSEMBLER__ 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) if (use_dsp) { channels = 2; sample_size = 16; - freq = NATIVE_FREQUENCY; + freq = dsp_get_output_frequency(ci.dsp); type = WAVE_FORMAT_PCM; } else { channels = format.channels; @@ -312,7 +312,7 @@ static void playback_start(void) { playback_running = true; SDL_AudioSpec spec = {0}; - spec.freq = NATIVE_FREQUENCY; + spec.freq = dsp_get_output_frequency(ci.dsp); spec.format = AUDIO_S16SYS; spec.channels = 2; spec.samples = 0x400; @@ -776,6 +776,7 @@ static void decode_file(const char *input_fn) ci.id3 = &id3; if (use_dsp) { ci.dsp = dsp_get_config(CODEC_IDX_AUDIO); + dsp_configure(ci.dsp, DSP_SET_OUT_FREQUENCY, DSP_OUT_DEFAULT_HZ); dsp_configure(ci.dsp, DSP_RESET, 0); dsp_dither_enable(false); }