diff --git a/apps/plugin.c b/apps/plugin.c index bfb7f0e9c1..6b3d39973f 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -781,6 +781,7 @@ static const struct plugin_api rockbox_api = { #if CONFIG_CODEC == SWCODEC mixer_channel_status, mixer_channel_get_buffer, + mixer_channel_calculate_peaks, #endif }; diff --git a/apps/plugin.h b/apps/plugin.h index aa39829daf..77c8e831d4 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -146,7 +146,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 206 +#define PLUGIN_API_VERSION 207 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any @@ -911,6 +911,8 @@ struct plugin_api { #if CONFIG_CODEC == SWCODEC enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel); void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel, int *count); + void (*mixer_channel_calculate_peaks)(enum pcm_mixer_channel channel, + int *left, int *right); #endif }; diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c index 07bf1da8bb..5eb43fae62 100644 --- a/apps/plugins/oscilloscope.c +++ b/apps/plugins/oscilloscope.c @@ -826,7 +826,8 @@ enum plugin_status plugin_start(const void* parameter) left = rb->mas_codec_readreg(0xC); right = rb->mas_codec_readreg(0xD); #elif (CONFIG_CODEC == SWCODEC) - rb->pcm_calculate_peaks(&left, &right); + rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK, + &left, &right); #endif if (osc.orientation == OSC_HORIZ) anim_horizontal(left, right); diff --git a/apps/plugins/starfield.c b/apps/plugins/starfield.c index 5e832aa38a..e7bbb425be 100644 --- a/apps/plugins/starfield.c +++ b/apps/plugins/starfield.c @@ -422,7 +422,8 @@ int plugin_main(void) /* Get the peaks. ( Borrowed from vu_meter ) */ #if (CONFIG_CODEC == SWCODEC) int left_peak, right_peak; - rb->pcm_calculate_peaks(&left_peak, &right_peak); + rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK, + &left_peak, &right_peak); #else int left_peak = rb->mas_codec_readreg(0xC); int right_peak = rb->mas_codec_readreg(0xD); diff --git a/apps/plugins/vu_meter.c b/apps/plugins/vu_meter.c index 5266214723..cb1a035678 100644 --- a/apps/plugins/vu_meter.c +++ b/apps/plugins/vu_meter.c @@ -660,7 +660,8 @@ void analog_meter(void) { int right_peak = rb->mas_codec_readreg(0xD); #elif (CONFIG_CODEC == SWCODEC) int left_peak, right_peak; - rb->pcm_calculate_peaks(&left_peak, &right_peak); + rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK, + &left_peak, &right_peak); #endif if(vumeter_settings.analog_use_db_scale) { diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c index c13c4c9539..fd8f8d9fbe 100644 --- a/apps/recorder/peakmeter.c +++ b/apps/recorder/peakmeter.c @@ -44,6 +44,7 @@ #if CONFIG_CODEC == SWCODEC #include "pcm.h" +#include "pcm_mixer.h" #ifdef HAVE_RECORDING #include "pcm_record.h" @@ -620,7 +621,8 @@ void peak_meter_peek(void) /* read current values */ #if CONFIG_CODEC == SWCODEC if (pm_playback) - pcm_calculate_peaks(&pm_cur_left, &pm_cur_right); + mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK, + &pm_cur_left, &pm_cur_right); #ifdef HAVE_RECORDING else pcm_calculate_rec_peaks(&pm_cur_left, &pm_cur_right); diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h index d69138f534..bae6a368fa 100644 --- a/firmware/export/pcm-internal.h +++ b/firmware/export/pcm-internal.h @@ -22,6 +22,16 @@ #ifndef PCM_INTERNAL_H #define PCM_INTERNAL_H +struct pcm_peaks +{ + long period; + long tick; + uint16_t val[2]; +}; + +void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active, + const void *addr, int count); + /** The following are for internal use between pcm.c and target- specific portion **/ diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h index 9c4e06e0be..be10601ffd 100644 --- a/firmware/export/pcm_mixer.h +++ b/firmware/export/pcm_mixer.h @@ -103,6 +103,10 @@ size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel); /* Return pointer to channel's playing audio data and the size remaining */ void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count); +/* Calculate peak values for channel */ +void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel, + int *left, int *right); + /* Stop ALL channels and PCM and reset state */ void mixer_reset(void); diff --git a/firmware/pcm.c b/firmware/pcm.c index b7415f329b..c2ebc67687 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c @@ -93,6 +93,9 @@ unsigned long pcm_sampr SHAREDBSS_ATTR = HW_SAMPR_DEFAULT; /* samplerate frequency selection index */ int pcm_fsel SHAREDBSS_ATTR = HW_FREQ_DEFAULT; +/* peak data for the global peak values - i.e. what the final output is */ +static struct pcm_peaks global_peaks; + /* Called internally by functions to reset the state */ static void pcm_play_stopped(void) { @@ -107,7 +110,7 @@ static void pcm_play_stopped(void) * * Used for recording and playback. */ -static void pcm_peak_peeker(const int32_t *addr, int count, int peaks[2]) +static void pcm_peak_peeker(const int32_t *addr, int count, uint16_t peaks[2]) { int peak_l = 0, peak_r = 0; const int32_t * const end = addr + count; @@ -145,18 +148,13 @@ static void pcm_peak_peeker(const int32_t *addr, int count, int peaks[2]) peaks[1] = peak_r; } -void pcm_calculate_peaks(int *left, int *right) +void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active, + const void *addr, int count) { - static int peaks[2] = { 0, 0 }; - static unsigned long last_peak_tick = 0; - static unsigned long frame_period = 0; - long tick = current_tick; - int count; - const void *addr; - /* Throttled peak ahead based on calling period */ - long period = tick - last_peak_tick; + /* Peak no farther ahead than expected period to avoid overcalculation */ + long period = tick - peaks->tick; /* Keep reasonable limits on period */ if (period < 1) @@ -164,33 +162,38 @@ void pcm_calculate_peaks(int *left, int *right) else if (period > HZ/5) period = HZ/5; - frame_period = (3*frame_period + period) >> 2; + peaks->period = (3*peaks->period + period) >> 2; + peaks->tick = tick; - last_peak_tick = tick; - - addr = pcm_play_dma_get_peak_buffer(&count); - - if (pcm_playing && !pcm_paused) + if (active) { - int framecount; - - framecount = frame_period*pcm_curr_sampr / HZ; + int framecount = peaks->period*pcm_curr_sampr / HZ; count = MIN(framecount, count); if (count > 0) - pcm_peak_peeker((int32_t *)addr, count, peaks); + pcm_peak_peeker((int32_t *)addr, count, peaks->val); /* else keep previous peak values */ } else { - peaks[0] = peaks[1] = 0; + /* peaks are zero */ + peaks->val[0] = peaks->val[1] = 0; } +} + +void pcm_calculate_peaks(int *left, int *right) +{ + int count; + const void *addr = pcm_play_dma_get_peak_buffer(&count); + + pcm_do_peak_calculation(&global_peaks, pcm_playing && !pcm_paused, + addr, count); if (left) - *left = peaks[0]; + *left = global_peaks.val[0]; if (right) - *right = peaks[1]; + *right = global_peaks.val[1]; } const void* pcm_get_peak_buffer(int * count) @@ -437,7 +440,7 @@ static void pcm_recording_stopped(void) */ void pcm_calculate_rec_peaks(int *left, int *right) { - static int peaks[2]; + static uint16_t peaks[2]; if (pcm_recording) { @@ -484,10 +487,8 @@ void pcm_init_recording(void) { logf("pcm_init_recording"); -#ifndef HAVE_PCM_FULL_DUPLEX /* Stop the beasty before attempting recording */ mixer_reset(); -#endif /* Recording init is locked unlike general pcm init since this is not * just a one-time event at startup and it should and must be safe by diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c index 69a43cfbda..c84762938e 100644 --- a/firmware/pcm_mixer.c +++ b/firmware/pcm_mixer.c @@ -23,6 +23,7 @@ #include "general.h" #include "kernel.h" #include "pcm.h" +#include "pcm-internal.h" #include "pcm_mixer.h" #include "dsp.h" @@ -59,6 +60,9 @@ static size_t next_size = 0; /* Size of buffer to play next time */ /* Descriptors for all available channels */ static struct mixer_channel channels[PCM_MIXER_NUM_CHANNELS] IBSS_ATTR; +/* History for channel peaks */ +static struct pcm_peaks channel_peaks[PCM_MIXER_NUM_CHANNELS]; + /* Packed pointer array of all playing (active) channels in "channels" array */ static struct mixer_channel * active_channels[PCM_MIXER_NUM_CHANNELS+1] IBSS_ATTR; @@ -343,11 +347,14 @@ static void mixer_start_pcm(void) if (pcm_is_playing()) return; -#if defined(HAVE_RECORDING) && !defined(HAVE_PCM_FULL_DUPLEX) +#if defined(HAVE_RECORDING) if (pcm_is_recording()) return; #endif + /* Requires a shared global sample rate for all channels */ + pcm_set_frequency(NATIVE_FREQUENCY); + /* Prepare initial frames and set up the double buffer */ mixer_buffer_callback(); @@ -492,6 +499,25 @@ void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count) return NULL; } +/* Calculate peak values for channel */ +void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel, + int *left, int *right) +{ + struct mixer_channel *chan = &channels[channel]; + struct pcm_peaks *peaks = &channel_peaks[channel]; + int count; + const void *addr = mixer_channel_get_buffer(channel, &count); + + pcm_do_peak_calculation(peaks, chan->status == CHANNEL_PLAYING, + addr, count); + + if (left) + *left = peaks->val[0]; + + if (right) + *right = peaks->val[1]; +} + /* Stop ALL channels and PCM and reset state */ void mixer_reset(void) {