summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugin.c5
-rw-r--r--apps/plugin.h9
-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
10 files changed, 298 insertions, 76 deletions
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 = {
586#endif 586#endif
587 sound_unit, 587 sound_unit,
588 sound_val2phys, 588 sound_val2phys,
589 dsp_set_crossfeed,
590 dsp_set_eq,
591 dsp_dither_enable,
592 dsp_configure,
593 dsp_process,
589#endif /* CONFIG_CODEC == SWCODEC */ 594#endif /* CONFIG_CODEC == SWCODEC */
590}; 595};
591 596
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 @@
119#define PLUGIN_MAGIC 0x526F634B /* RocK */ 119#define PLUGIN_MAGIC 0x526F634B /* RocK */
120 120
121/* increase this every time the api struct changes */ 121/* increase this every time the api struct changes */
122#define PLUGIN_API_VERSION 98 122#define PLUGIN_API_VERSION 99
123 123
124/* update this to latest version if a change to the api struct breaks 124/* update this to latest version if a change to the api struct breaks
125 backwards compatibility (and please take the opportunity to sort in any 125 backwards compatibility (and please take the opportunity to sort in any
@@ -723,6 +723,13 @@ struct plugin_api {
723#endif 723#endif
724 const char * (*sound_unit)(int setting); 724 const char * (*sound_unit)(int setting);
725 int (*sound_val2phys)(int setting, int value); 725 int (*sound_val2phys)(int setting, int value);
726 void (*dsp_set_crossfeed)(bool enable);
727 void (*dsp_set_eq)(bool enable);
728 void (*dsp_dither_enable)(bool enable);
729 intptr_t (*dsp_configure)(struct dsp_config *dsp, int setting,
730 intptr_t value);
731 int (*dsp_process)(struct dsp_config *dsp, char *dest,
732 const char *src[], int count);
726#endif /* CONFIG_CODEC == SWCODEC */ 733#endif /* CONFIG_CODEC == SWCODEC */
727}; 734};
728 735
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 {