diff --git a/apps/plugin.c b/apps/plugin.c index 1f773cfd2d..6862ddb08e 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -586,6 +586,11 @@ static const struct plugin_api rockbox_api = { #endif sound_unit, sound_val2phys, + dsp_set_crossfeed, + dsp_set_eq, + dsp_dither_enable, + dsp_configure, + dsp_process, #endif /* CONFIG_CODEC == SWCODEC */ }; diff --git a/apps/plugin.h b/apps/plugin.h index f91d803854..164a2c9847 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -119,7 +119,7 @@ #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 98 +#define PLUGIN_API_VERSION 99 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any @@ -723,6 +723,13 @@ struct plugin_api { #endif const char * (*sound_unit)(int setting); int (*sound_val2phys)(int setting, int value); + void (*dsp_set_crossfeed)(bool enable); + void (*dsp_set_eq)(bool enable); + void (*dsp_dither_enable)(bool enable); + intptr_t (*dsp_configure)(struct dsp_config *dsp, int setting, + intptr_t value); + int (*dsp_process)(struct dsp_config *dsp, char *dest, + const char *src[], int count); #endif /* CONFIG_CODEC == SWCODEC */ }; diff --git a/apps/plugins/mpegplayer/audio_thread.c b/apps/plugins/mpegplayer/audio_thread.c index 78d28e40c5..2bb766ad88 100644 --- a/apps/plugins/mpegplayer/audio_thread.c +++ b/apps/plugins/mpegplayer/audio_thread.c @@ -27,10 +27,13 @@ struct pts_queue_slot; struct audio_thread_data { - struct queue_event ev; /* Our event queue to receive commands */ - int state; /* Thread state */ - int status; /* Media status (STREAM_PLAYING, etc.) */ - int mad_errors; /* A count of the errors in each frame */ + struct queue_event ev; /* Our event queue to receive commands */ + int state; /* Thread state */ + int status; /* Media status (STREAM_PLAYING, etc.) */ + int mad_errors; /* A count of the errors in each frame */ + unsigned samplerate; /* Current stream sample rate */ + int nchannels; /* Number of audio channels */ + struct dsp_config *dsp; /* The DSP we're using */ }; /* The audio stack is stolen from the core codec thread (but not in uisim) */ @@ -402,6 +405,8 @@ static void audio_thread_msg(struct audio_thread_data *td) td->status = STREAM_STOPPED; td->state = TSTATE_INIT; + td->samplerate = 0; + td->nchannels = 0; init_mad(); td->mad_errors = 0; @@ -460,12 +465,19 @@ static void audio_thread(void) { struct audio_thread_data td; + rb->memset(&td, 0, sizeof (td)); td.status = STREAM_STOPPED; td.state = TSTATE_EOS; /* We need this here to init the EMAC for Coldfire targets */ init_mad(); + td.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP, + CODEC_IDX_AUDIO); + rb->sound_set_pitch(1000); + rb->dsp_configure(td.dsp, DSP_RESET, 0); + rb->dsp_configure(td.dsp, DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS); + goto message_wait; /* This is the decoding loop. */ @@ -594,64 +606,56 @@ static void audio_thread(void) mad_synth_frame(&synth, &frame); /** Output **/ + if (frame.header.samplerate != td.samplerate) + { + td.samplerate = frame.header.samplerate; + rb->dsp_configure(td.dsp, DSP_SWITCH_FREQUENCY, + td.samplerate); + } - /* TODO: Output through core dsp. We'll still use our own PCM buffer - since the core pcm buffer has no timestamping or clock facilities */ + if (MAD_NCHANNELS(&frame.header) != td.nchannels) + { + td.nchannels = MAD_NCHANNELS(&frame.header); + rb->dsp_configure(td.dsp, DSP_SET_STEREO_MODE, + td.nchannels == 1 ? + STEREO_MONO : STEREO_NONINTERLEAVED); + } + + td.state = TSTATE_RENDER_WAIT; /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */ render_wait: if (synth.pcm.length > 0) { - struct pcm_frame_header *pcm_insert = pcm_output_get_buffer(); - int16_t *audio_data = (int16_t *)pcm_insert->data; - unsigned length = synth.pcm.length; - ssize_t size = sizeof(*pcm_insert) + length*4; - - td.state = TSTATE_RENDER_WAIT; + struct pcm_frame_header *dst_hdr = pcm_output_get_buffer(); + const char *src[2] = + { (char *)synth.pcm.samples[0], (char *)synth.pcm.samples[1] }; + int out_count = (synth.pcm.length * CLOCK_RATE + + (td.samplerate - 1)) / td.samplerate; + ssize_t size = sizeof(*dst_hdr) + out_count*4; /* Wait for required amount of free buffer space */ while (pcm_output_free() < size) { /* Wait one frame */ - int timeout = synth.pcm.length*HZ / synth.pcm.samplerate; + int timeout = out_count*HZ / td.samplerate; str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1)); if (td.ev.id != SYS_TIMEOUT) goto message_process; } - pcm_insert->time = audio_queue.curr->time; - pcm_insert->size = size; + out_count = rb->dsp_process(td.dsp, dst_hdr->data, src, + synth.pcm.length); - /* As long as we're on this timestamp, the time is just incremented - by the number of samples */ - audio_queue.curr->time += length; + if (out_count <= 0) + break; - if (MAD_NCHANNELS(&frame.header) == 2) - { - int32_t *left = &synth.pcm.samples[0][0]; - int32_t *right = &synth.pcm.samples[1][0]; + dst_hdr->size = sizeof(*dst_hdr) + out_count*4; + dst_hdr->time = audio_queue.curr->time; - do - { - /* libmad outputs s3.28 */ - *audio_data++ = clip_sample(*left++ >> 13); - *audio_data++ = clip_sample(*right++ >> 13); - } - while (--length > 0); - } - else /* mono */ - { - int32_t *mono = &synth.pcm.samples[0][0]; - - do - { - int32_t s = clip_sample(*mono++ >> 13); - *audio_data++ = s; - *audio_data++ = s; - } - while (--length > 0); - } - /**/ + /* As long as we're on this timestamp, the time is just + incremented by the number of samples */ + audio_queue.curr->time += out_count; /* Make this data available to DMA */ pcm_output_add_data(); @@ -712,9 +716,10 @@ bool audio_thread_init(void) rb->queue_init(audio_str.hdr.q, false); rb->queue_enable_queue_send(audio_str.hdr.q, &audio_str_queue_send); + /* One-up on the priority since the core DSP over-yields internally */ audio_str.thread = rb->create_thread( audio_thread, audio_stack, audio_stack_size, 0, - "mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, CPU)); + "mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK-1) IF_COP(, CPU)); if (audio_str.thread == NULL) return false; diff --git a/apps/plugins/mpegplayer/mpeg_misc.h b/apps/plugins/mpegplayer/mpeg_misc.h index 2da9c2e313..6c5a41655b 100644 --- a/apps/plugins/mpegplayer/mpeg_misc.h +++ b/apps/plugins/mpegplayer/mpeg_misc.h @@ -168,15 +168,6 @@ void stream_scan_normalize(struct stream_scan *sk); * direction, otherwise opposite the scan direction */ void stream_scan_offset(struct stream_scan *sk, off_t by); -/** Audio helpers **/ -static inline int32_t clip_sample(int32_t sample) -{ - if ((int16_t)sample != sample) - sample = 0x7fff ^ (sample >> 31); - - return sample; -} - /** Time helpers **/ struct hms { diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c index fa8fad8b4c..fc99460d74 100644 --- a/apps/plugins/mpegplayer/mpeg_settings.c +++ b/apps/plugins/mpegplayer/mpeg_settings.c @@ -109,6 +109,11 @@ static struct configdata config[] = {TYPE_INT, 0, INT_MAX, &settings.displayoptions, "Display options", NULL, NULL}, #endif + {TYPE_INT, 0, 2, &settings.tone_controls, "Tone controls", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.channel_modes, "Channel modes", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.crossfeed, "Crossfeed", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.equalizer, "Equalizer", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.dithering, "Dithering", NULL, NULL}, }; static const struct opt_items noyes[2] = { @@ -121,6 +126,11 @@ static const struct opt_items enabledisable[2] = { { "Enable", -1 }, }; +static const struct opt_items globaloff[2] = { + { "Force off", -1 }, + { "Use sound setting", -1 }, +}; + static long mpeg_menu_sysevent_id; void mpeg_menu_sysevent_clear(void) @@ -184,6 +194,70 @@ static bool mpeg_set_option(const char* string, return usb; } +/* Sync a particular audio setting to global or mpegplayer forced off */ +static void sync_audio_setting(int setting, bool global) +{ + int val0, val1; + + switch (setting) + { + case MPEG_AUDIO_TONE_CONTROLS: + if (global || settings.tone_controls) + { + val0 = rb->global_settings->bass; + val1 = rb->global_settings->treble; + } + else + { + val0 = rb->sound_default(SOUND_BASS); + val1 = rb->sound_default(SOUND_TREBLE); + } + rb->sound_set(SOUND_BASS, val0); + rb->sound_set(SOUND_TREBLE, val1); + break; + + case MPEG_AUDIO_CHANNEL_MODES: + val0 = (global || settings.channel_modes) ? + rb->global_settings->channel_config : + SOUND_CHAN_STEREO; + rb->sound_set(SOUND_CHANNELS, val0); + break; + + case MPEG_AUDIO_CROSSFEED: + rb->dsp_set_crossfeed((global || settings.crossfeed) ? + rb->global_settings->crossfeed : false); + break; + + case MPEG_AUDIO_EQUALIZER: + rb->dsp_set_eq((global || settings.equalizer) ? + rb->global_settings->eq_enabled : false); + break; + + case MPEG_AUDIO_DITHERING: + rb->dsp_dither_enable((global || settings.dithering) ? + rb->global_settings->dithering_enabled : false); + break; + } +} + +/* Sync all audio settings to global or mpegplayer forced off */ +static void sync_audio_settings(bool global) +{ + static const int setting_index[] = + { + MPEG_AUDIO_TONE_CONTROLS, + MPEG_AUDIO_CHANNEL_MODES, + MPEG_AUDIO_CROSSFEED, + MPEG_AUDIO_EQUALIZER, + MPEG_AUDIO_DITHERING, + }; + unsigned i; + + for (i = 0; i < ARRAYLEN(setting_index); i++) + { + sync_audio_setting(setting_index[i], global); + } +} #ifndef HAVE_LCD_COLOR /* Cheapo splash implementation for the grey surface */ @@ -736,6 +810,79 @@ static void display_options(void) menu_exit(menu_id); } +static void audio_options(void) +{ + int result; + int menu_id; + bool menu_quit = false; + + static const struct menu_item items[] = { + [MPEG_AUDIO_TONE_CONTROLS] = + { "Tone Controls", NULL }, + [MPEG_AUDIO_CHANNEL_MODES] = + { "Channel Modes", NULL }, + [MPEG_AUDIO_CROSSFEED] = + { "Crossfeed", NULL }, + [MPEG_AUDIO_EQUALIZER] = + { "Equalizer", NULL }, + [MPEG_AUDIO_DITHERING] = + { "Dithering", NULL }, + }; + + menu_id = menu_init(rb, items, ARRAYLEN(items), + mpeg_menu_sysevent_callback, NULL, NULL, NULL); + + rb->button_clear_queue(); + + while (!menu_quit) + { + mpeg_menu_sysevent_clear(); + result = menu_show(menu_id); + + switch (result) + { + case MPEG_AUDIO_TONE_CONTROLS: + mpeg_set_option("Tone Controls", &settings.tone_controls, INT, + globaloff, 2, NULL); + sync_audio_setting(result, false); + break; + + case MPEG_AUDIO_CHANNEL_MODES: + mpeg_set_option("Channel Modes", &settings.channel_modes, + INT, globaloff, 2, NULL); + sync_audio_setting(result, false); + break; + + case MPEG_AUDIO_CROSSFEED: + mpeg_set_option("Crossfeed", &settings.crossfeed, INT, + globaloff, 2, NULL); + sync_audio_setting(result, false); + break; + + case MPEG_AUDIO_EQUALIZER: + mpeg_set_option("Equalizer", &settings.equalizer, INT, + globaloff, 2, NULL); + sync_audio_setting(result, false); + break; + + case MPEG_AUDIO_DITHERING: + mpeg_set_option("Dithering", &settings.dithering, INT, + globaloff, 2, NULL); + sync_audio_setting(result, false); + break; + + default: + menu_quit = true; + break; + } + + if (mpeg_menu_sysevent() != 0) + menu_quit = true; + } + + menu_exit(menu_id); +} + static void resume_options(void) { static const struct opt_items items[MPEG_RESUME_NUM_OPTIONS] = { @@ -775,6 +922,8 @@ int mpeg_menu(unsigned flags) struct menu_item items[] = { [MPEG_MENU_DISPLAY_SETTINGS] = { "Display Options", NULL }, + [MPEG_MENU_AUDIO_SETTINGS] = + { "Audio Options", NULL }, [MPEG_MENU_ENABLE_START_MENU] = { "Resume Options", NULL }, [MPEG_MENU_CLEAR_RESUMES] = @@ -809,6 +958,10 @@ int mpeg_menu(unsigned flags) display_options(); break; + case MPEG_MENU_AUDIO_SETTINGS: + audio_options(); + break; + case MPEG_MENU_ENABLE_START_MENU: resume_options(); break; @@ -849,6 +1002,11 @@ void init_settings(const char* filename) #if MPEG_OPTION_DITHERING_ENABLED settings.displayoptions = 0; /* No visual effects */ #endif + settings.tone_controls = false; + settings.channel_modes = false; + settings.crossfeed = false; + settings.equalizer = false; + settings.dithering = false; configfile_init(rb); @@ -886,6 +1044,9 @@ void init_settings(const char* filename) { settings.resume_time = 0; } + + /* Set our audio options */ + sync_audio_settings(false); } void save_settings(void) @@ -911,4 +1072,17 @@ void save_settings(void) configfile_update_entry(SETTINGS_FILENAME, "Display options", settings.displayoptions); #endif + configfile_update_entry(SETTINGS_FILENAME, "Tone controls", + settings.tone_controls); + configfile_update_entry(SETTINGS_FILENAME, "Channel modes", + settings.channel_modes); + configfile_update_entry(SETTINGS_FILENAME, "Crossfeed", + settings.crossfeed); + configfile_update_entry(SETTINGS_FILENAME, "Equalizer", + settings.equalizer); + configfile_update_entry(SETTINGS_FILENAME, "Dithering", + settings.dithering); + + /* Restore audio options */ + sync_audio_settings(true); } diff --git a/apps/plugins/mpegplayer/mpeg_settings.h b/apps/plugins/mpegplayer/mpeg_settings.h index 4d6da478ea..1557ff433e 100644 --- a/apps/plugins/mpegplayer/mpeg_settings.h +++ b/apps/plugins/mpegplayer/mpeg_settings.h @@ -1,7 +1,7 @@ #include "plugin.h" -#define SETTINGS_VERSION 3 +#define SETTINGS_VERSION 4 #define SETTINGS_MIN_VERSION 1 #define SETTINGS_FILENAME "mpegplayer.cfg" @@ -24,6 +24,15 @@ enum mpeg_option_id MPEG_OPTION_SKIP_FRAMES, }; +enum mpeg_audio_option_id +{ + MPEG_AUDIO_TONE_CONTROLS, + MPEG_AUDIO_CHANNEL_MODES, + MPEG_AUDIO_CROSSFEED, + MPEG_AUDIO_EQUALIZER, + MPEG_AUDIO_DITHERING, +}; + enum mpeg_resume_id { MPEG_RESUME_MENU_ALWAYS = 0, @@ -46,6 +55,7 @@ enum mpeg_start_id enum mpeg_menu_id { MPEG_MENU_DISPLAY_SETTINGS, + MPEG_MENU_AUDIO_SETTINGS, MPEG_MENU_ENABLE_START_MENU, MPEG_MENU_CLEAR_RESUMES, MPEG_MENU_QUIT, @@ -62,6 +72,12 @@ struct mpeg_settings { #if MPEG_OPTION_DITHERING_ENABLED int displayoptions; #endif + /* Audio options - simple on/off specification */ + int tone_controls; + int channel_modes; + int crossfeed; + int equalizer; + int dithering; }; extern struct mpeg_settings settings; diff --git a/apps/plugins/mpegplayer/mpegplayer.h b/apps/plugins/mpegplayer/mpegplayer.h index 11bc1ea669..b92af38e6c 100644 --- a/apps/plugins/mpegplayer/mpegplayer.h +++ b/apps/plugins/mpegplayer/mpegplayer.h @@ -53,18 +53,19 @@ enum mpeg_malloc_reason_t #define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE) /** PCM buffer **/ -#define CLOCK_RATE 44100 /* Our clock rate in ticks/second (samplerate) */ +#define CLOCK_RATE NATIVE_FREQUENCY /* 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 -#define PCMOUT_BUFSIZE (CLOCK_RATE) /* 1s */ -#define PCMOUT_GUARD_SIZE (1152*4 + sizeof (struct pcm_frame_header)) -#define PCMOUT_ALLOC_SIZE (PCMOUT_BUFSIZE + PCMOUT_GUARD_SIZE) - /* Start pcm playback @ 25% full */ -#define PCMOUT_PLAY_WM (PCMOUT_BUFSIZE/4) - /* No valid audio frame is smaller */ -#define PCMOUT_LOW_WM (sizeof (struct pcm_frame_header)) +#define PCMOUT_BUFSIZE (CLOCK_RATE/2*4) /* 1/2s */ +#define PCMOUT_GUARD_SAMPLES ((CLOCK_RATE*576+7999)/8000) /* Worst upsampling case */ +#define PCMOUT_GUARD_SIZE (PCMOUT_GUARD_SAMPLES*4 + sizeof (struct pcm_frame_header)) +#define PCMOUT_ALLOC_SIZE (PCMOUT_BUFSIZE + PCMOUT_GUARD_SIZE) + /* Start pcm playback @ 25% full */ +#define PCMOUT_PLAY_WM (PCMOUT_BUFSIZE/4) + /* No valid audio frame is smaller */ +#define PCMOUT_LOW_WM (sizeof (struct pcm_frame_header)) /** disk buffer **/ #define DISK_BUF_LOW_WATERMARK (1024*1024) diff --git a/apps/plugins/mpegplayer/pcm_output.c b/apps/plugins/mpegplayer/pcm_output.c index ac89308af1..e17f635f72 100644 --- a/apps/plugins/mpegplayer/pcm_output.c +++ b/apps/plugins/mpegplayer/pcm_output.c @@ -33,6 +33,7 @@ static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_head IBSS_ATTR; static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR; /* Bytes */ +static ssize_t pcmbuf_curr_size IBSS_ATTR; /* Size of currently playing frame */ static uint64_t pcmbuf_read IBSS_ATTR; /* Number of bytes read by DMA */ static uint64_t pcmbuf_written IBSS_ATTR; /* Number of bytes written by source */ static ssize_t pcmbuf_threshold IBSS_ATTR; /* Non-silence threshold */ @@ -42,6 +43,9 @@ static uint32_t clock_base IBSS_ATTR; /* Our base clock */ static uint32_t clock_start IBSS_ATTR; /* Clock at playback start */ static int32_t clock_adjust IBSS_ATTR; /* Clock drift adjustment */ +int pcm_skipped = 0; +int pcm_underruns = 0; + /* Small silence clip. ~5.80ms @ 44.1kHz */ static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 }; @@ -51,7 +55,7 @@ static inline void pcm_advance_buffer(struct pcm_frame_header **p, { *p = SKIPBYTES(*p, size); if (*p >= pcmbuf_end) - *p = pcm_buffer; + *p = SKIPBYTES(*p, -PCMOUT_BUFSIZE); } /* Inline internally but not externally */ @@ -68,7 +72,13 @@ inline ssize_t pcm_output_free(void) /* Audio DMA handler */ static void get_more(unsigned char **start, size_t *size) { - ssize_t sz = pcm_output_used(); + ssize_t sz; + + /* Free-up the last frame played frame if any */ + pcmbuf_read += pcmbuf_curr_size; + pcmbuf_curr_size = 0; + + sz = pcm_output_used(); if (sz > pcmbuf_threshold) { @@ -95,16 +105,20 @@ static void get_more(unsigned char **start, size_t *size) /* Frame more than 100ms late - drop it */ pcm_advance_buffer(&pcmbuf_head, sz); pcmbuf_read += sz; + pcm_skipped++; if (pcmbuf_read < pcmbuf_written) continue; + + /* Ran out so revert to default watermark */ + pcmbuf_threshold = PCMOUT_PLAY_WM; } else if (offset < 100*CLOCK_RATE/1000) { /* Frame less than 100ms early - play it */ - *start = (unsigned char *)pcmbuf_head->data; + *start = pcmbuf_head->data; pcm_advance_buffer(&pcmbuf_head, sz); - pcmbuf_read += sz; + pcmbuf_curr_size = sz; sz -= sizeof (struct pcm_frame_header); @@ -124,6 +138,9 @@ static void get_more(unsigned char **start, size_t *size) else { /* Ran out so revert to default watermark */ + if (pcmbuf_threshold == PCMOUT_LOW_WM) + pcm_underruns++; + pcmbuf_threshold = PCMOUT_PLAY_WM; } @@ -164,8 +181,9 @@ void pcm_output_flush(void) rb->pcm_play_stop(); pcmbuf_threshold = PCMOUT_PLAY_WM; - pcmbuf_read = pcmbuf_written = 0; + pcmbuf_curr_size = pcmbuf_read = pcmbuf_written = 0; pcmbuf_head = pcmbuf_tail = pcm_buffer; + pcm_skipped = pcm_underruns = 0; /* Restart if playing state was current */ if (playing && !paused) @@ -251,10 +269,9 @@ bool pcm_output_init(void) pcmbuf_head = pcm_buffer; pcmbuf_tail = pcm_buffer; pcmbuf_end = SKIPBYTES(pcm_buffer, PCMOUT_BUFSIZE); - pcmbuf_read = 0; - pcmbuf_written = 0; + pcmbuf_curr_size = pcmbuf_read = pcmbuf_written = 0; - rb->pcm_set_frequency(SAMPR_44); + rb->pcm_set_frequency(NATIVE_FREQUENCY); #if INPUT_SRC_CAPS != 0 /* Select playback */ @@ -264,7 +281,7 @@ bool pcm_output_init(void) #if SILENCE_TEST_TONE /* Make the silence clip a square wave */ - const int16_t silence_amp = 32767 / 16; + const int16_t silence_amp = INT16_MAX / 16; unsigned i; for (i = 0; i < ARRAYLEN(silence); i += 2) diff --git a/apps/plugins/mpegplayer/pcm_output.h b/apps/plugins/mpegplayer/pcm_output.h index 8a230b87a5..94ca6eed3b 100644 --- a/apps/plugins/mpegplayer/pcm_output.h +++ b/apps/plugins/mpegplayer/pcm_output.h @@ -29,6 +29,8 @@ struct pcm_frame_header /* Header added to pcm data every time a decoded unsigned char data[]; /* open array of audio data */ } ALIGNED_ATTR(4); +extern int pcm_skipped, pcm_underruns; + bool pcm_output_init(void); void pcm_output_exit(void); void pcm_output_flush(void); diff --git a/apps/plugins/mpegplayer/video_thread.c b/apps/plugins/mpegplayer/video_thread.c index feee643d12..6508d28d1d 100644 --- a/apps/plugins/mpegplayer/video_thread.c +++ b/apps/plugins/mpegplayer/video_thread.c @@ -63,15 +63,21 @@ static void draw_fps(struct video_thread_data *td) uint32_t start; uint32_t clock_ticks = stream_get_ticks(&start); int fps = 0; + int buf_pct; char str[80]; clock_ticks -= start; if (clock_ticks != 0) fps = muldiv_uint32(CLOCK_RATE*100, td->num_drawn, clock_ticks); - rb->snprintf(str, sizeof(str), "%d.%02d %d %d ", + buf_pct = muldiv_uint32(100, pcm_output_used(), PCMOUT_BUFSIZE); + + rb->snprintf(str, sizeof(str), "v:%d.%02d %d %d a:%02d%% %d %d ", + /* Video information */ fps / 100, fps % 100, td->num_skipped, - td->info->display_picture->temporal_reference); + td->info->display_picture->temporal_reference, + /* Audio information */ + buf_pct, pcm_underruns, pcm_skipped); lcd_(putsxy)(0, 0, str); vo_lock(); @@ -277,7 +283,6 @@ static int sync_decoder(struct video_thread_data *td, while (1) { mpeg2_state_t mp2state = mpeg2_parse(td->mpeg2dec); - rb->yield(); switch (mp2state) { @@ -697,7 +702,6 @@ static void video_thread(void) picture_decode: mp2state = mpeg2_parse (td.mpeg2dec); - rb->yield(); switch (mp2state) {