summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-02-01 02:25:15 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-02-01 02:25:15 +0000
commitf90cbcb652af3bf794ec61d7f7ec3de00c8b7cb2 (patch)
tree62c13ffdeb5112181897103a6a015cfc1fab98ae /apps/plugins/mpegplayer
parent7e402d8202af409a0ea8f3f2676a2e6f501af05b (diff)
downloadrockbox-f90cbcb652af3bf794ec61d7f7ec3de00c8b7cb2.tar.gz
rockbox-f90cbcb652af3bf794ec61d7f7ec3de00c8b7cb2.zip
mpegplayer: Use the core DSP to process audio. Removes the sample rate restriction on audio and any mpeg audio samplerate may be used. Use the global sound settings for audio output with the option to force any one of the processing stages off. All are forced off by default. I didn't personally care to fully duplicate the Sound Settings menu which would have been needed since using the core one would affect settings globally and exactly the same configuration probably isn't desired since the CPU load for video playback is much greater. Rebalance the threading to compensate with some expense to buffering speed.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@16194 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mpegplayer')
-rw-r--r--apps/plugins/mpegplayer/audio_thread.c93
-rw-r--r--apps/plugins/mpegplayer/mpeg_misc.h9
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c174
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.h18
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.h17
-rw-r--r--apps/plugins/mpegplayer/pcm_output.c35
-rw-r--r--apps/plugins/mpegplayer/pcm_output.h2
-rw-r--r--apps/plugins/mpegplayer/video_thread.c12
8 files changed, 285 insertions, 75 deletions
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 @@
27struct pts_queue_slot; 27struct pts_queue_slot;
28struct audio_thread_data 28struct audio_thread_data
29{ 29{
30 struct queue_event ev; /* Our event queue to receive commands */ 30 struct queue_event ev; /* Our event queue to receive commands */
31 int state; /* Thread state */ 31 int state; /* Thread state */
32 int status; /* Media status (STREAM_PLAYING, etc.) */ 32 int status; /* Media status (STREAM_PLAYING, etc.) */
33 int mad_errors; /* A count of the errors in each frame */ 33 int mad_errors; /* A count of the errors in each frame */
34 unsigned samplerate; /* Current stream sample rate */
35 int nchannels; /* Number of audio channels */
36 struct dsp_config *dsp; /* The DSP we're using */
34}; 37};
35 38
36/* The audio stack is stolen from the core codec thread (but not in uisim) */ 39/* 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)
402 405
403 td->status = STREAM_STOPPED; 406 td->status = STREAM_STOPPED;
404 td->state = TSTATE_INIT; 407 td->state = TSTATE_INIT;
408 td->samplerate = 0;
409 td->nchannels = 0;
405 410
406 init_mad(); 411 init_mad();
407 td->mad_errors = 0; 412 td->mad_errors = 0;
@@ -460,12 +465,19 @@ static void audio_thread(void)
460{ 465{
461 struct audio_thread_data td; 466 struct audio_thread_data td;
462 467
468 rb->memset(&td, 0, sizeof (td));
463 td.status = STREAM_STOPPED; 469 td.status = STREAM_STOPPED;
464 td.state = TSTATE_EOS; 470 td.state = TSTATE_EOS;
465 471
466 /* We need this here to init the EMAC for Coldfire targets */ 472 /* We need this here to init the EMAC for Coldfire targets */
467 init_mad(); 473 init_mad();
468 474
475 td.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP,
476 CODEC_IDX_AUDIO);
477 rb->sound_set_pitch(1000);
478 rb->dsp_configure(td.dsp, DSP_RESET, 0);
479 rb->dsp_configure(td.dsp, DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS);
480
469 goto message_wait; 481 goto message_wait;
470 482
471 /* This is the decoding loop. */ 483 /* This is the decoding loop. */
@@ -594,64 +606,56 @@ static void audio_thread(void)
594 mad_synth_frame(&synth, &frame); 606 mad_synth_frame(&synth, &frame);
595 607
596 /** Output **/ 608 /** Output **/
609 if (frame.header.samplerate != td.samplerate)
610 {
611 td.samplerate = frame.header.samplerate;
612 rb->dsp_configure(td.dsp, DSP_SWITCH_FREQUENCY,
613 td.samplerate);
614 }
597 615
598 /* TODO: Output through core dsp. We'll still use our own PCM buffer 616 if (MAD_NCHANNELS(&frame.header) != td.nchannels)
599 since the core pcm buffer has no timestamping or clock facilities */ 617 {
618 td.nchannels = MAD_NCHANNELS(&frame.header);
619 rb->dsp_configure(td.dsp, DSP_SET_STEREO_MODE,
620 td.nchannels == 1 ?
621 STEREO_MONO : STEREO_NONINTERLEAVED);
622 }
623
624 td.state = TSTATE_RENDER_WAIT;
600 625
601 /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */ 626 /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */
602 render_wait: 627 render_wait:
603 if (synth.pcm.length > 0) 628 if (synth.pcm.length > 0)
604 { 629 {
605 struct pcm_frame_header *pcm_insert = pcm_output_get_buffer(); 630 struct pcm_frame_header *dst_hdr = pcm_output_get_buffer();
606 int16_t *audio_data = (int16_t *)pcm_insert->data; 631 const char *src[2] =
607 unsigned length = synth.pcm.length; 632 { (char *)synth.pcm.samples[0], (char *)synth.pcm.samples[1] };
608 ssize_t size = sizeof(*pcm_insert) + length*4; 633 int out_count = (synth.pcm.length * CLOCK_RATE
609 634 + (td.samplerate - 1)) / td.samplerate;
610 td.state = TSTATE_RENDER_WAIT; 635 ssize_t size = sizeof(*dst_hdr) + out_count*4;
611 636
612 /* Wait for required amount of free buffer space */ 637 /* Wait for required amount of free buffer space */
613 while (pcm_output_free() < size) 638 while (pcm_output_free() < size)
614 { 639 {
615 /* Wait one frame */ 640 /* Wait one frame */
616 int timeout = synth.pcm.length*HZ / synth.pcm.samplerate; 641 int timeout = out_count*HZ / td.samplerate;
617 str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1)); 642 str_get_msg_w_tmo(&audio_str, &td.ev, MAX(timeout, 1));
618 if (td.ev.id != SYS_TIMEOUT) 643 if (td.ev.id != SYS_TIMEOUT)
619 goto message_process; 644 goto message_process;
620 } 645 }
621 646
622 pcm_insert->time = audio_queue.curr->time; 647 out_count = rb->dsp_process(td.dsp, dst_hdr->data, src,
623 pcm_insert->size = size; 648 synth.pcm.length);
624 649
625 /* As long as we're on this timestamp, the time is just incremented 650 if (out_count <= 0)
626 by the number of samples */ 651 break;
627 audio_queue.curr->time += length;
628
629 if (MAD_NCHANNELS(&frame.header) == 2)
630 {
631 int32_t *left = &synth.pcm.samples[0][0];
632 int32_t *right = &synth.pcm.samples[1][0];
633 652
634 do 653 dst_hdr->size = sizeof(*dst_hdr) + out_count*4;
635 { 654 dst_hdr->time = audio_queue.curr->time;
636 /* libmad outputs s3.28 */
637 *audio_data++ = clip_sample(*left++ >> 13);
638 *audio_data++ = clip_sample(*right++ >> 13);
639 }
640 while (--length > 0);
641 }
642 else /* mono */
643 {
644 int32_t *mono = &synth.pcm.samples[0][0];
645 655
646 do 656 /* As long as we're on this timestamp, the time is just
647 { 657 incremented by the number of samples */
648 int32_t s = clip_sample(*mono++ >> 13); 658 audio_queue.curr->time += out_count;
649 *audio_data++ = s;
650 *audio_data++ = s;
651 }
652 while (--length > 0);
653 }
654 /**/
655 659
656 /* Make this data available to DMA */ 660 /* Make this data available to DMA */
657 pcm_output_add_data(); 661 pcm_output_add_data();
@@ -712,9 +716,10 @@ bool audio_thread_init(void)
712 rb->queue_init(audio_str.hdr.q, false); 716 rb->queue_init(audio_str.hdr.q, false);
713 rb->queue_enable_queue_send(audio_str.hdr.q, &audio_str_queue_send); 717 rb->queue_enable_queue_send(audio_str.hdr.q, &audio_str_queue_send);
714 718
719 /* One-up on the priority since the core DSP over-yields internally */
715 audio_str.thread = rb->create_thread( 720 audio_str.thread = rb->create_thread(
716 audio_thread, audio_stack, audio_stack_size, 0, 721 audio_thread, audio_stack, audio_stack_size, 0,
717 "mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, CPU)); 722 "mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK-1) IF_COP(, CPU));
718 723
719 if (audio_str.thread == NULL) 724 if (audio_str.thread == NULL)
720 return false; 725 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);
168 * direction, otherwise opposite the scan direction */ 168 * direction, otherwise opposite the scan direction */
169void stream_scan_offset(struct stream_scan *sk, off_t by); 169void stream_scan_offset(struct stream_scan *sk, off_t by);
170 170
171/** Audio helpers **/
172static inline int32_t clip_sample(int32_t sample)
173{
174 if ((int16_t)sample != sample)
175 sample = 0x7fff ^ (sample >> 31);
176
177 return sample;
178}
179
180/** Time helpers **/ 171/** Time helpers **/
181struct hms 172struct hms
182{ 173{
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[] =
109 {TYPE_INT, 0, INT_MAX, &settings.displayoptions, "Display options", 109 {TYPE_INT, 0, INT_MAX, &settings.displayoptions, "Display options",
110 NULL, NULL}, 110 NULL, NULL},
111#endif 111#endif
112 {TYPE_INT, 0, 2, &settings.tone_controls, "Tone controls", NULL, NULL},
113 {TYPE_INT, 0, 2, &settings.channel_modes, "Channel modes", NULL, NULL},
114 {TYPE_INT, 0, 2, &settings.crossfeed, "Crossfeed", NULL, NULL},
115 {TYPE_INT, 0, 2, &settings.equalizer, "Equalizer", NULL, NULL},
116 {TYPE_INT, 0, 2, &settings.dithering, "Dithering", NULL, NULL},
112}; 117};
113 118
114static const struct opt_items noyes[2] = { 119static const struct opt_items noyes[2] = {
@@ -121,6 +126,11 @@ static const struct opt_items enabledisable[2] = {
121 { "Enable", -1 }, 126 { "Enable", -1 },
122}; 127};
123 128
129static const struct opt_items globaloff[2] = {
130 { "Force off", -1 },
131 { "Use sound setting", -1 },
132};
133
124static long mpeg_menu_sysevent_id; 134static long mpeg_menu_sysevent_id;
125 135
126void mpeg_menu_sysevent_clear(void) 136void mpeg_menu_sysevent_clear(void)
@@ -184,6 +194,70 @@ static bool mpeg_set_option(const char* string,
184 return usb; 194 return usb;
185} 195}
186 196
197/* Sync a particular audio setting to global or mpegplayer forced off */
198static void sync_audio_setting(int setting, bool global)
199{
200 int val0, val1;
201
202 switch (setting)
203 {
204 case MPEG_AUDIO_TONE_CONTROLS:
205 if (global || settings.tone_controls)
206 {
207 val0 = rb->global_settings->bass;
208 val1 = rb->global_settings->treble;
209 }
210 else
211 {
212 val0 = rb->sound_default(SOUND_BASS);
213 val1 = rb->sound_default(SOUND_TREBLE);
214 }
215 rb->sound_set(SOUND_BASS, val0);
216 rb->sound_set(SOUND_TREBLE, val1);
217 break;
218
219 case MPEG_AUDIO_CHANNEL_MODES:
220 val0 = (global || settings.channel_modes) ?
221 rb->global_settings->channel_config :
222 SOUND_CHAN_STEREO;
223 rb->sound_set(SOUND_CHANNELS, val0);
224 break;
225
226 case MPEG_AUDIO_CROSSFEED:
227 rb->dsp_set_crossfeed((global || settings.crossfeed) ?
228 rb->global_settings->crossfeed : false);
229 break;
230
231 case MPEG_AUDIO_EQUALIZER:
232 rb->dsp_set_eq((global || settings.equalizer) ?
233 rb->global_settings->eq_enabled : false);
234 break;
235
236 case MPEG_AUDIO_DITHERING:
237 rb->dsp_dither_enable((global || settings.dithering) ?
238 rb->global_settings->dithering_enabled : false);
239 break;
240 }
241}
242
243/* Sync all audio settings to global or mpegplayer forced off */
244static void sync_audio_settings(bool global)
245{
246 static const int setting_index[] =
247 {
248 MPEG_AUDIO_TONE_CONTROLS,
249 MPEG_AUDIO_CHANNEL_MODES,
250 MPEG_AUDIO_CROSSFEED,
251 MPEG_AUDIO_EQUALIZER,
252 MPEG_AUDIO_DITHERING,
253 };
254 unsigned i;
255
256 for (i = 0; i < ARRAYLEN(setting_index); i++)
257 {
258 sync_audio_setting(setting_index[i], global);
259 }
260}
187 261
188#ifndef HAVE_LCD_COLOR 262#ifndef HAVE_LCD_COLOR
189/* Cheapo splash implementation for the grey surface */ 263/* Cheapo splash implementation for the grey surface */
@@ -736,6 +810,79 @@ static void display_options(void)
736 menu_exit(menu_id); 810 menu_exit(menu_id);
737} 811}
738 812
813static void audio_options(void)
814{
815 int result;
816 int menu_id;
817 bool menu_quit = false;
818
819 static const struct menu_item items[] = {
820 [MPEG_AUDIO_TONE_CONTROLS] =
821 { "Tone Controls", NULL },
822 [MPEG_AUDIO_CHANNEL_MODES] =
823 { "Channel Modes", NULL },
824 [MPEG_AUDIO_CROSSFEED] =
825 { "Crossfeed", NULL },
826 [MPEG_AUDIO_EQUALIZER] =
827 { "Equalizer", NULL },
828 [MPEG_AUDIO_DITHERING] =
829 { "Dithering", NULL },
830 };
831
832 menu_id = menu_init(rb, items, ARRAYLEN(items),
833 mpeg_menu_sysevent_callback, NULL, NULL, NULL);
834
835 rb->button_clear_queue();
836
837 while (!menu_quit)
838 {
839 mpeg_menu_sysevent_clear();
840 result = menu_show(menu_id);
841
842 switch (result)
843 {
844 case MPEG_AUDIO_TONE_CONTROLS:
845 mpeg_set_option("Tone Controls", &settings.tone_controls, INT,
846 globaloff, 2, NULL);
847 sync_audio_setting(result, false);
848 break;
849
850 case MPEG_AUDIO_CHANNEL_MODES:
851 mpeg_set_option("Channel Modes", &settings.channel_modes,
852 INT, globaloff, 2, NULL);
853 sync_audio_setting(result, false);
854 break;
855
856 case MPEG_AUDIO_CROSSFEED:
857 mpeg_set_option("Crossfeed", &settings.crossfeed, INT,
858 globaloff, 2, NULL);
859 sync_audio_setting(result, false);
860 break;
861
862 case MPEG_AUDIO_EQUALIZER:
863 mpeg_set_option("Equalizer", &settings.equalizer, INT,
864 globaloff, 2, NULL);
865 sync_audio_setting(result, false);
866 break;
867
868 case MPEG_AUDIO_DITHERING:
869 mpeg_set_option("Dithering", &settings.dithering, INT,
870 globaloff, 2, NULL);
871 sync_audio_setting(result, false);
872 break;
873
874 default:
875 menu_quit = true;
876 break;
877 }
878
879 if (mpeg_menu_sysevent() != 0)
880 menu_quit = true;
881 }
882
883 menu_exit(menu_id);
884}
885
739static void resume_options(void) 886static void resume_options(void)
740{ 887{
741 static const struct opt_items items[MPEG_RESUME_NUM_OPTIONS] = { 888 static const struct opt_items items[MPEG_RESUME_NUM_OPTIONS] = {
@@ -775,6 +922,8 @@ int mpeg_menu(unsigned flags)
775 struct menu_item items[] = { 922 struct menu_item items[] = {
776 [MPEG_MENU_DISPLAY_SETTINGS] = 923 [MPEG_MENU_DISPLAY_SETTINGS] =
777 { "Display Options", NULL }, 924 { "Display Options", NULL },
925 [MPEG_MENU_AUDIO_SETTINGS] =
926 { "Audio Options", NULL },
778 [MPEG_MENU_ENABLE_START_MENU] = 927 [MPEG_MENU_ENABLE_START_MENU] =
779 { "Resume Options", NULL }, 928 { "Resume Options", NULL },
780 [MPEG_MENU_CLEAR_RESUMES] = 929 [MPEG_MENU_CLEAR_RESUMES] =
@@ -809,6 +958,10 @@ int mpeg_menu(unsigned flags)
809 display_options(); 958 display_options();
810 break; 959 break;
811 960
961 case MPEG_MENU_AUDIO_SETTINGS:
962 audio_options();
963 break;
964
812 case MPEG_MENU_ENABLE_START_MENU: 965 case MPEG_MENU_ENABLE_START_MENU:
813 resume_options(); 966 resume_options();
814 break; 967 break;
@@ -849,6 +1002,11 @@ void init_settings(const char* filename)
849#if MPEG_OPTION_DITHERING_ENABLED 1002#if MPEG_OPTION_DITHERING_ENABLED
850 settings.displayoptions = 0; /* No visual effects */ 1003 settings.displayoptions = 0; /* No visual effects */
851#endif 1004#endif
1005 settings.tone_controls = false;
1006 settings.channel_modes = false;
1007 settings.crossfeed = false;
1008 settings.equalizer = false;
1009 settings.dithering = false;
852 1010
853 configfile_init(rb); 1011 configfile_init(rb);
854 1012
@@ -886,6 +1044,9 @@ void init_settings(const char* filename)
886 { 1044 {
887 settings.resume_time = 0; 1045 settings.resume_time = 0;
888 } 1046 }
1047
1048 /* Set our audio options */
1049 sync_audio_settings(false);
889} 1050}
890 1051
891void save_settings(void) 1052void save_settings(void)
@@ -911,4 +1072,17 @@ void save_settings(void)
911 configfile_update_entry(SETTINGS_FILENAME, "Display options", 1072 configfile_update_entry(SETTINGS_FILENAME, "Display options",
912 settings.displayoptions); 1073 settings.displayoptions);
913#endif 1074#endif
1075 configfile_update_entry(SETTINGS_FILENAME, "Tone controls",
1076 settings.tone_controls);
1077 configfile_update_entry(SETTINGS_FILENAME, "Channel modes",
1078 settings.channel_modes);
1079 configfile_update_entry(SETTINGS_FILENAME, "Crossfeed",
1080 settings.crossfeed);
1081 configfile_update_entry(SETTINGS_FILENAME, "Equalizer",
1082 settings.equalizer);
1083 configfile_update_entry(SETTINGS_FILENAME, "Dithering",
1084 settings.dithering);
1085
1086 /* Restore audio options */
1087 sync_audio_settings(true);
914} 1088}
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 @@
1 1
2#include "plugin.h" 2#include "plugin.h"
3 3
4#define SETTINGS_VERSION 3 4#define SETTINGS_VERSION 4
5#define SETTINGS_MIN_VERSION 1 5#define SETTINGS_MIN_VERSION 1
6#define SETTINGS_FILENAME "mpegplayer.cfg" 6#define SETTINGS_FILENAME "mpegplayer.cfg"
7 7
@@ -24,6 +24,15 @@ enum mpeg_option_id
24 MPEG_OPTION_SKIP_FRAMES, 24 MPEG_OPTION_SKIP_FRAMES,
25}; 25};
26 26
27enum mpeg_audio_option_id
28{
29 MPEG_AUDIO_TONE_CONTROLS,
30 MPEG_AUDIO_CHANNEL_MODES,
31 MPEG_AUDIO_CROSSFEED,
32 MPEG_AUDIO_EQUALIZER,
33 MPEG_AUDIO_DITHERING,
34};
35
27enum mpeg_resume_id 36enum mpeg_resume_id
28{ 37{
29 MPEG_RESUME_MENU_ALWAYS = 0, 38 MPEG_RESUME_MENU_ALWAYS = 0,
@@ -46,6 +55,7 @@ enum mpeg_start_id
46enum mpeg_menu_id 55enum mpeg_menu_id
47{ 56{
48 MPEG_MENU_DISPLAY_SETTINGS, 57 MPEG_MENU_DISPLAY_SETTINGS,
58 MPEG_MENU_AUDIO_SETTINGS,
49 MPEG_MENU_ENABLE_START_MENU, 59 MPEG_MENU_ENABLE_START_MENU,
50 MPEG_MENU_CLEAR_RESUMES, 60 MPEG_MENU_CLEAR_RESUMES,
51 MPEG_MENU_QUIT, 61 MPEG_MENU_QUIT,
@@ -62,6 +72,12 @@ struct mpeg_settings {
62#if MPEG_OPTION_DITHERING_ENABLED 72#if MPEG_OPTION_DITHERING_ENABLED
63 int displayoptions; 73 int displayoptions;
64#endif 74#endif
75 /* Audio options - simple on/off specification */
76 int tone_controls;
77 int channel_modes;
78 int crossfeed;
79 int equalizer;
80 int dithering;
65}; 81};
66 82
67extern struct mpeg_settings settings; 83extern 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
53#define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE) 53#define AUDIOBUF_ALLOC_SIZE (AUDIOBUF_SIZE+AUDIOBUF_GUARD_SIZE)
54 54
55/** PCM buffer **/ 55/** PCM buffer **/
56#define CLOCK_RATE 44100 /* Our clock rate in ticks/second (samplerate) */ 56#define CLOCK_RATE NATIVE_FREQUENCY /* Our clock rate in ticks/second (samplerate) */
57 57
58/* Define this as "1" to have a test tone instead of silence clip */ 58/* Define this as "1" to have a test tone instead of silence clip */
59#define SILENCE_TEST_TONE 0 59#define SILENCE_TEST_TONE 0
60 60
61#define PCMOUT_BUFSIZE (CLOCK_RATE) /* 1s */ 61#define PCMOUT_BUFSIZE (CLOCK_RATE/2*4) /* 1/2s */
62#define PCMOUT_GUARD_SIZE (1152*4 + sizeof (struct pcm_frame_header)) 62#define PCMOUT_GUARD_SAMPLES ((CLOCK_RATE*576+7999)/8000) /* Worst upsampling case */
63#define PCMOUT_ALLOC_SIZE (PCMOUT_BUFSIZE + PCMOUT_GUARD_SIZE) 63#define PCMOUT_GUARD_SIZE (PCMOUT_GUARD_SAMPLES*4 + sizeof (struct pcm_frame_header))
64 /* Start pcm playback @ 25% full */ 64#define PCMOUT_ALLOC_SIZE (PCMOUT_BUFSIZE + PCMOUT_GUARD_SIZE)
65#define PCMOUT_PLAY_WM (PCMOUT_BUFSIZE/4) 65 /* Start pcm playback @ 25% full */
66 /* No valid audio frame is smaller */ 66#define PCMOUT_PLAY_WM (PCMOUT_BUFSIZE/4)
67#define PCMOUT_LOW_WM (sizeof (struct pcm_frame_header)) 67 /* No valid audio frame is smaller */
68#define PCMOUT_LOW_WM (sizeof (struct pcm_frame_header))
68 69
69/** disk buffer **/ 70/** disk buffer **/
70#define DISK_BUF_LOW_WATERMARK (1024*1024) 71#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;
33static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR; 33static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR;
34 34
35/* Bytes */ 35/* Bytes */
36static ssize_t pcmbuf_curr_size IBSS_ATTR; /* Size of currently playing frame */
36static uint64_t pcmbuf_read IBSS_ATTR; /* Number of bytes read by DMA */ 37static uint64_t pcmbuf_read IBSS_ATTR; /* Number of bytes read by DMA */
37static uint64_t pcmbuf_written IBSS_ATTR; /* Number of bytes written by source */ 38static uint64_t pcmbuf_written IBSS_ATTR; /* Number of bytes written by source */
38static ssize_t pcmbuf_threshold IBSS_ATTR; /* Non-silence threshold */ 39static ssize_t pcmbuf_threshold IBSS_ATTR; /* Non-silence threshold */
@@ -42,6 +43,9 @@ static uint32_t clock_base IBSS_ATTR; /* Our base clock */
42static uint32_t clock_start IBSS_ATTR; /* Clock at playback start */ 43static uint32_t clock_start IBSS_ATTR; /* Clock at playback start */
43static int32_t clock_adjust IBSS_ATTR; /* Clock drift adjustment */ 44static int32_t clock_adjust IBSS_ATTR; /* Clock drift adjustment */
44 45
46int pcm_skipped = 0;
47int pcm_underruns = 0;
48
45/* Small silence clip. ~5.80ms @ 44.1kHz */ 49/* Small silence clip. ~5.80ms @ 44.1kHz */
46static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 }; 50static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 };
47 51
@@ -51,7 +55,7 @@ static inline void pcm_advance_buffer(struct pcm_frame_header **p,
51{ 55{
52 *p = SKIPBYTES(*p, size); 56 *p = SKIPBYTES(*p, size);
53 if (*p >= pcmbuf_end) 57 if (*p >= pcmbuf_end)
54 *p = pcm_buffer; 58 *p = SKIPBYTES(*p, -PCMOUT_BUFSIZE);
55} 59}
56 60
57/* Inline internally but not externally */ 61/* Inline internally but not externally */
@@ -68,7 +72,13 @@ inline ssize_t pcm_output_free(void)
68/* Audio DMA handler */ 72/* Audio DMA handler */
69static void get_more(unsigned char **start, size_t *size) 73static void get_more(unsigned char **start, size_t *size)
70{ 74{
71 ssize_t sz = pcm_output_used(); 75 ssize_t sz;
76
77 /* Free-up the last frame played frame if any */
78 pcmbuf_read += pcmbuf_curr_size;
79 pcmbuf_curr_size = 0;
80
81 sz = pcm_output_used();
72 82
73 if (sz > pcmbuf_threshold) 83 if (sz > pcmbuf_threshold)
74 { 84 {
@@ -95,16 +105,20 @@ static void get_more(unsigned char **start, size_t *size)
95 /* Frame more than 100ms late - drop it */ 105 /* Frame more than 100ms late - drop it */
96 pcm_advance_buffer(&pcmbuf_head, sz); 106 pcm_advance_buffer(&pcmbuf_head, sz);
97 pcmbuf_read += sz; 107 pcmbuf_read += sz;
108 pcm_skipped++;
98 if (pcmbuf_read < pcmbuf_written) 109 if (pcmbuf_read < pcmbuf_written)
99 continue; 110 continue;
111
112 /* Ran out so revert to default watermark */
113 pcmbuf_threshold = PCMOUT_PLAY_WM;
100 } 114 }
101 else if (offset < 100*CLOCK_RATE/1000) 115 else if (offset < 100*CLOCK_RATE/1000)
102 { 116 {
103 /* Frame less than 100ms early - play it */ 117 /* Frame less than 100ms early - play it */
104 *start = (unsigned char *)pcmbuf_head->data; 118 *start = pcmbuf_head->data;
105 119
106 pcm_advance_buffer(&pcmbuf_head, sz); 120 pcm_advance_buffer(&pcmbuf_head, sz);
107 pcmbuf_read += sz; 121 pcmbuf_curr_size = sz;
108 122
109 sz -= sizeof (struct pcm_frame_header); 123 sz -= sizeof (struct pcm_frame_header);
110 124
@@ -124,6 +138,9 @@ static void get_more(unsigned char **start, size_t *size)
124 else 138 else
125 { 139 {
126 /* Ran out so revert to default watermark */ 140 /* Ran out so revert to default watermark */
141 if (pcmbuf_threshold == PCMOUT_LOW_WM)
142 pcm_underruns++;
143
127 pcmbuf_threshold = PCMOUT_PLAY_WM; 144 pcmbuf_threshold = PCMOUT_PLAY_WM;
128 } 145 }
129 146
@@ -164,8 +181,9 @@ void pcm_output_flush(void)
164 rb->pcm_play_stop(); 181 rb->pcm_play_stop();
165 182
166 pcmbuf_threshold = PCMOUT_PLAY_WM; 183 pcmbuf_threshold = PCMOUT_PLAY_WM;
167 pcmbuf_read = pcmbuf_written = 0; 184 pcmbuf_curr_size = pcmbuf_read = pcmbuf_written = 0;
168 pcmbuf_head = pcmbuf_tail = pcm_buffer; 185 pcmbuf_head = pcmbuf_tail = pcm_buffer;
186 pcm_skipped = pcm_underruns = 0;
169 187
170 /* Restart if playing state was current */ 188 /* Restart if playing state was current */
171 if (playing && !paused) 189 if (playing && !paused)
@@ -251,10 +269,9 @@ bool pcm_output_init(void)
251 pcmbuf_head = pcm_buffer; 269 pcmbuf_head = pcm_buffer;
252 pcmbuf_tail = pcm_buffer; 270 pcmbuf_tail = pcm_buffer;
253 pcmbuf_end = SKIPBYTES(pcm_buffer, PCMOUT_BUFSIZE); 271 pcmbuf_end = SKIPBYTES(pcm_buffer, PCMOUT_BUFSIZE);
254 pcmbuf_read = 0; 272 pcmbuf_curr_size = pcmbuf_read = pcmbuf_written = 0;
255 pcmbuf_written = 0;
256 273
257 rb->pcm_set_frequency(SAMPR_44); 274 rb->pcm_set_frequency(NATIVE_FREQUENCY);
258 275
259#if INPUT_SRC_CAPS != 0 276#if INPUT_SRC_CAPS != 0
260 /* Select playback */ 277 /* Select playback */
@@ -264,7 +281,7 @@ bool pcm_output_init(void)
264 281
265#if SILENCE_TEST_TONE 282#if SILENCE_TEST_TONE
266 /* Make the silence clip a square wave */ 283 /* Make the silence clip a square wave */
267 const int16_t silence_amp = 32767 / 16; 284 const int16_t silence_amp = INT16_MAX / 16;
268 unsigned i; 285 unsigned i;
269 286
270 for (i = 0; i < ARRAYLEN(silence); i += 2) 287 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
29 unsigned char data[]; /* open array of audio data */ 29 unsigned char data[]; /* open array of audio data */
30} ALIGNED_ATTR(4); 30} ALIGNED_ATTR(4);
31 31
32extern int pcm_skipped, pcm_underruns;
33
32bool pcm_output_init(void); 34bool pcm_output_init(void);
33void pcm_output_exit(void); 35void pcm_output_exit(void);
34void pcm_output_flush(void); 36void 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)
63 uint32_t start; 63 uint32_t start;
64 uint32_t clock_ticks = stream_get_ticks(&start); 64 uint32_t clock_ticks = stream_get_ticks(&start);
65 int fps = 0; 65 int fps = 0;
66 int buf_pct;
66 char str[80]; 67 char str[80];
67 68
68 clock_ticks -= start; 69 clock_ticks -= start;
69 if (clock_ticks != 0) 70 if (clock_ticks != 0)
70 fps = muldiv_uint32(CLOCK_RATE*100, td->num_drawn, clock_ticks); 71 fps = muldiv_uint32(CLOCK_RATE*100, td->num_drawn, clock_ticks);
71 72
72 rb->snprintf(str, sizeof(str), "%d.%02d %d %d ", 73 buf_pct = muldiv_uint32(100, pcm_output_used(), PCMOUT_BUFSIZE);
74
75 rb->snprintf(str, sizeof(str), "v:%d.%02d %d %d a:%02d%% %d %d ",
76 /* Video information */
73 fps / 100, fps % 100, td->num_skipped, 77 fps / 100, fps % 100, td->num_skipped,
74 td->info->display_picture->temporal_reference); 78 td->info->display_picture->temporal_reference,
79 /* Audio information */
80 buf_pct, pcm_underruns, pcm_skipped);
75 lcd_(putsxy)(0, 0, str); 81 lcd_(putsxy)(0, 0, str);
76 82
77 vo_lock(); 83 vo_lock();
@@ -277,7 +283,6 @@ static int sync_decoder(struct video_thread_data *td,
277 while (1) 283 while (1)
278 { 284 {
279 mpeg2_state_t mp2state = mpeg2_parse(td->mpeg2dec); 285 mpeg2_state_t mp2state = mpeg2_parse(td->mpeg2dec);
280 rb->yield();
281 286
282 switch (mp2state) 287 switch (mp2state)
283 { 288 {
@@ -697,7 +702,6 @@ static void video_thread(void)
697 702
698 picture_decode: 703 picture_decode:
699 mp2state = mpeg2_parse (td.mpeg2dec); 704 mp2state = mpeg2_parse (td.mpeg2dec);
700 rb->yield();
701 705
702 switch (mp2state) 706 switch (mp2state)
703 { 707 {