summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-11-18 17:12:19 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-11-18 17:12:19 +0000
commit99617d71bad0e5870a38e37c8654e46868e2a5ba (patch)
treee0ecc3b73e8e167c7f5bf00a6c88b83c1119aea3
parent75432619e8be2f22f86ed0869d46bf7245c7c14d (diff)
downloadrockbox-99617d71bad0e5870a38e37c8654e46868e2a5ba.tar.gz
rockbox-99617d71bad0e5870a38e37c8654e46868e2a5ba.zip
Make speex the new voice format for SWCODEC targets (non-Archos). Remove codec swapping and build speex voice decoding directly into the core binary.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15668 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/codecs.c4
-rw-r--r--apps/codecs.h4
-rw-r--r--apps/codecs/libspeex/bits.c6
-rw-r--r--apps/codecs/libspeex/config-speex.h6
-rw-r--r--apps/codecs/libspeex/exc_10_16_table.c2
-rw-r--r--apps/codecs/libspeex/exc_10_32_table.c2
-rw-r--r--apps/codecs/libspeex/exc_20_32_table.c2
-rw-r--r--apps/codecs/libspeex/exc_5_256_table.c2
-rw-r--r--apps/codecs/libspeex/exc_5_64_table.c2
-rw-r--r--apps/codecs/libspeex/exc_8_128_table.c2
-rw-r--r--apps/dsp.c290
-rw-r--r--apps/dsp.h26
-rw-r--r--apps/pcmbuf.c32
-rw-r--r--apps/pcmbuf.h6
-rw-r--r--apps/playback.c854
-rw-r--r--apps/plugin.c9
-rw-r--r--apps/plugin.h8
-rw-r--r--apps/talk.c10
-rw-r--r--apps/talk.h2
-rw-r--r--apps/voice_thread.c444
-rw-r--r--apps/voice_thread.h33
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/common/memswap128.c44
-rw-r--r--firmware/export/audio.h14
-rw-r--r--firmware/export/config.h2
-rw-r--r--firmware/target/arm/memswap128-arm.S44
-rw-r--r--firmware/target/coldfire/memswap128-coldfire.S50
-rw-r--r--tools/voicefont.c2
29 files changed, 753 insertions, 1153 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 5e097049c2..597eb48b23 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -15,6 +15,7 @@ menus/theme_menu.c
15#if CONFIG_CODEC == SWCODEC 15#if CONFIG_CODEC == SWCODEC
16menus/eq_menu.c 16menus/eq_menu.c
17buffering.c 17buffering.c
18voice_thread.c
18#endif 19#endif
19menus/main_menu.c 20menus/main_menu.c
20menus/playback_menu.c 21menus/playback_menu.c
diff --git a/apps/codecs.c b/apps/codecs.c
index 88ecd24ce4..f2539dc73e 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -67,8 +67,6 @@ extern unsigned char codecbuf[];
67 67
68extern void* plugin_get_audio_buffer(size_t *buffer_size); 68extern void* plugin_get_audio_buffer(size_t *buffer_size);
69 69
70struct codec_api ci_voice;
71
72struct codec_api ci = { 70struct codec_api ci = {
73 71
74 0, /* filesize */ 72 0, /* filesize */
@@ -163,6 +161,8 @@ struct codec_api ci = {
163 flush_icache, 161 flush_icache,
164 invalidate_icache, 162 invalidate_icache,
165#endif 163#endif
164
165 NULL, /* struct sp_data *dsp */
166}; 166};
167 167
168void codec_get_full_path(char *path, const char *codec_root_fn) 168void codec_get_full_path(char *path, const char *codec_root_fn)
diff --git a/apps/codecs.h b/apps/codecs.h
index ecba1e99ef..d2ba00ca2a 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -80,7 +80,7 @@
80#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ 80#define CODEC_ENC_MAGIC 0x52454E43 /* RENC */
81 81
82/* increase this every time the api struct changes */ 82/* increase this every time the api struct changes */
83#define CODEC_API_VERSION 19 83#define CODEC_API_VERSION 20
84 84
85/* update this to latest version if a change to the api struct breaks 85/* update this to latest version if a change to the api struct breaks
86 backwards compatibility (and please take the opportunity to sort in any 86 backwards compatibility (and please take the opportunity to sort in any
@@ -234,6 +234,8 @@ struct codec_api {
234 void (*flush_icache)(void); 234 void (*flush_icache)(void);
235 void (*invalidate_icache)(void); 235 void (*invalidate_icache)(void);
236#endif 236#endif
237
238 struct dsp_config *dsp;
237}; 239};
238 240
239/* codec header */ 241/* codec header */
diff --git a/apps/codecs/libspeex/bits.c b/apps/codecs/libspeex/bits.c
index e460a39cf2..4629012c57 100644
--- a/apps/codecs/libspeex/bits.c
+++ b/apps/codecs/libspeex/bits.c
@@ -45,6 +45,7 @@
45#define MAX_CHARS_PER_FRAME (2000/BYTES_PER_CHAR) 45#define MAX_CHARS_PER_FRAME (2000/BYTES_PER_CHAR)
46#endif 46#endif
47 47
48#ifndef ROCKBOX_VOICE_CODEC
48void speex_bits_init(SpeexBits *bits) 49void speex_bits_init(SpeexBits *bits)
49{ 50{
50 bits->chars = (char*)speex_alloc(MAX_CHARS_PER_FRAME); 51 bits->chars = (char*)speex_alloc(MAX_CHARS_PER_FRAME);
@@ -57,6 +58,7 @@ void speex_bits_init(SpeexBits *bits)
57 58
58 speex_bits_reset(bits); 59 speex_bits_reset(bits);
59} 60}
61#endif
60 62
61void speex_bits_init_buffer(SpeexBits *bits, void *buff, int buf_size) 63void speex_bits_init_buffer(SpeexBits *bits, void *buff, int buf_size)
62{ 64{
@@ -82,12 +84,14 @@ void speex_bits_set_bit_buffer(SpeexBits *bits, void *buff, int buf_size)
82 84
83} 85}
84 86
87#ifndef ROCKBOX_VOICE_CODEC
85void speex_bits_destroy(SpeexBits *bits) 88void speex_bits_destroy(SpeexBits *bits)
86{ 89{
87 if (bits->owner) 90 if (bits->owner)
88 speex_free(bits->chars); 91 speex_free(bits->chars);
89 /* Will do something once the allocation is dynamic */ 92 /* Will do something once the allocation is dynamic */
90} 93}
94#endif
91 95
92void speex_bits_reset(SpeexBits *bits) 96void speex_bits_reset(SpeexBits *bits)
93{ 97{
@@ -106,7 +110,7 @@ void speex_bits_rewind(SpeexBits *bits)
106 bits->overflow=0; 110 bits->overflow=0;
107} 111}
108 112
109#ifndef SPEEX_VOICE_ENCODER 113#if !defined(SPEEX_VOICE_ENCODER) && !defined(ROCKBOX_VOICE_CODEC)
110void speex_bits_read_from(SpeexBits *bits, char *chars, int len) 114void speex_bits_read_from(SpeexBits *bits, char *chars, int len)
111{ 115{
112 int i; 116 int i;
diff --git a/apps/codecs/libspeex/config-speex.h b/apps/codecs/libspeex/config-speex.h
index ad1393fc60..70d86f6299 100644
--- a/apps/codecs/libspeex/config-speex.h
+++ b/apps/codecs/libspeex/config-speex.h
@@ -45,6 +45,12 @@
45#define FLOATING_POINT 45#define FLOATING_POINT
46#endif 46#endif
47 47
48#ifndef ROCKBOX_VOICE_CODEC
49#define EXC_ICONST_ATTR ICONST_ATTR
50#else
51#define EXC_ICONST_ATTR
52#endif
53
48/* Define to 1 if you have the <dlfcn.h> header file. */ 54/* Define to 1 if you have the <dlfcn.h> header file. */
49/* #undef HAVE_DLFCN_H */ 55/* #undef HAVE_DLFCN_H */
50 56
diff --git a/apps/codecs/libspeex/exc_10_16_table.c b/apps/codecs/libspeex/exc_10_16_table.c
index 2184e9c955..755c5a0b7f 100644
--- a/apps/codecs/libspeex/exc_10_16_table.c
+++ b/apps/codecs/libspeex/exc_10_16_table.c
@@ -32,7 +32,7 @@
32 32
33#include "config-speex.h" 33#include "config-speex.h"
34 34
35const signed char exc_10_16_table[160] ICONST_ATTR = { 35const signed char exc_10_16_table[160] EXC_ICONST_ATTR = {
3622,39,14,44,11,35,-2,23,-4,6, 3622,39,14,44,11,35,-2,23,-4,6,
3746,-28,13,-27,-23,12,4,20,-5,9, 3746,-28,13,-27,-23,12,4,20,-5,9,
3837,-18,-23,23,0,9,-6,-20,4,-1, 3837,-18,-23,23,0,9,-6,-20,4,-1,
diff --git a/apps/codecs/libspeex/exc_10_32_table.c b/apps/codecs/libspeex/exc_10_32_table.c
index ac8cda03c8..1c94b5511c 100644
--- a/apps/codecs/libspeex/exc_10_32_table.c
+++ b/apps/codecs/libspeex/exc_10_32_table.c
@@ -32,7 +32,7 @@
32 32
33#include "config-speex.h" 33#include "config-speex.h"
34 34
35const signed char exc_10_32_table[320] ICONST_ATTR = { 35const signed char exc_10_32_table[320] EXC_ICONST_ATTR = {
367,17,17,27,25,22,12,4,-3,0, 367,17,17,27,25,22,12,4,-3,0,
3728,-36,39,-24,-15,3,-9,15,-5,10, 3728,-36,39,-24,-15,3,-9,15,-5,10,
3831,-28,11,31,-21,9,-11,-11,-2,-7, 3831,-28,11,31,-21,9,-11,-11,-2,-7,
diff --git a/apps/codecs/libspeex/exc_20_32_table.c b/apps/codecs/libspeex/exc_20_32_table.c
index fff3bed944..40dbb34e9e 100644
--- a/apps/codecs/libspeex/exc_20_32_table.c
+++ b/apps/codecs/libspeex/exc_20_32_table.c
@@ -32,7 +32,7 @@
32 32
33#include "config-speex.h" 33#include "config-speex.h"
34 34
35const signed char exc_20_32_table[640] ICONST_ATTR = { 35const signed char exc_20_32_table[640] EXC_ICONST_ATTR = {
3612,32,25,46,36,33,9,14,-3,6,1,-8,0,-10,-5,-7,-7,-7,-5,-5, 3612,32,25,46,36,33,9,14,-3,6,1,-8,0,-10,-5,-7,-7,-7,-5,-5,
3731,-27,24,-32,-4,10,-11,21,-3,19,23,-9,22,24,-10,-1,-10,-13,-7,-11, 3731,-27,24,-32,-4,10,-11,21,-3,19,23,-9,22,24,-10,-1,-10,-13,-7,-11,
3842,-33,31,19,-8,0,-10,-16,1,-21,-17,10,-8,14,8,4,11,-2,5,-2, 3842,-33,31,19,-8,0,-10,-16,1,-21,-17,10,-8,14,8,4,11,-2,5,-2,
diff --git a/apps/codecs/libspeex/exc_5_256_table.c b/apps/codecs/libspeex/exc_5_256_table.c
index 6af987fea4..1a32057956 100644
--- a/apps/codecs/libspeex/exc_5_256_table.c
+++ b/apps/codecs/libspeex/exc_5_256_table.c
@@ -32,7 +32,7 @@
32 32
33#include "config-speex.h" 33#include "config-speex.h"
34 34
35const signed char exc_5_256_table[1280] ICONST_ATTR = { 35const signed char exc_5_256_table[1280] EXC_ICONST_ATTR = {
36-8,-37,5,-43,5, 36-8,-37,5,-43,5,
3773,61,39,12,-3, 3773,61,39,12,-3,
38-61,-32,2,42,30, 38-61,-32,2,42,30,
diff --git a/apps/codecs/libspeex/exc_5_64_table.c b/apps/codecs/libspeex/exc_5_64_table.c
index cd03eb5a20..7d29f60373 100644
--- a/apps/codecs/libspeex/exc_5_64_table.c
+++ b/apps/codecs/libspeex/exc_5_64_table.c
@@ -32,7 +32,7 @@
32 32
33#include "config-speex.h" 33#include "config-speex.h"
34 34
35const signed char exc_5_64_table[320] ICONST_ATTR = { 35const signed char exc_5_64_table[320] EXC_ICONST_ATTR = {
361,5,-15,49,-66, 361,5,-15,49,-66,
37-48,-4,50,-44,7, 37-48,-4,50,-44,7,
3837,16,-18,25,-26, 3837,16,-18,25,-26,
diff --git a/apps/codecs/libspeex/exc_8_128_table.c b/apps/codecs/libspeex/exc_8_128_table.c
index 3a910bee37..02a58e052c 100644
--- a/apps/codecs/libspeex/exc_8_128_table.c
+++ b/apps/codecs/libspeex/exc_8_128_table.c
@@ -32,7 +32,7 @@
32 32
33#include "config-speex.h" 33#include "config-speex.h"
34 34
35const signed char exc_8_128_table[1024] ICONST_ATTR = { 35const signed char exc_8_128_table[1024] EXC_ICONST_ATTR = {
36-14,9,13,-32,2,-10,31,-10, 36-14,9,13,-32,2,-10,31,-10,
37-8,-8,6,-4,-1,10,-64,23, 37-8,-8,6,-4,-1,10,-64,23,
386,20,13,6,8,-22,16,34, 386,20,13,6,8,-22,16,34,
diff --git a/apps/dsp.c b/apps/dsp.c
index 4cade936b9..6b2c698532 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -162,6 +162,10 @@ struct dsp_config
162 int sample_bytes; 162 int sample_bytes;
163 int stereo_mode; 163 int stereo_mode;
164 int frac_bits; 164 int frac_bits;
165#ifdef HAVE_SW_TONE_CONTROLS
166 /* Filter struct for software bass/treble controls */
167 struct eqfilter tone_filter;
168#endif
165 /* Functions that change depending upon settings - NULL if stage is 169 /* Functions that change depending upon settings - NULL if stage is
166 disabled */ 170 disabled */
167 sample_input_fn_type input_samples; 171 sample_input_fn_type input_samples;
@@ -171,6 +175,7 @@ struct dsp_config
171 way */ 175 way */
172 channels_process_dsp_fn_type apply_gain; 176 channels_process_dsp_fn_type apply_gain;
173 channels_process_fn_type apply_crossfeed; 177 channels_process_fn_type apply_crossfeed;
178 channels_process_fn_type eq_process;
174 channels_process_fn_type channels_process; 179 channels_process_fn_type channels_process;
175}; 180};
176 181
@@ -187,13 +192,13 @@ struct crossfeed_data crossfeed_data IDATA_ATTR = /* A */
187}; 192};
188 193
189/* Equalizer */ 194/* Equalizer */
190static struct eq_state eq_data; /* A/V */ 195static struct eq_state eq_data; /* A */
196
197/* Software tone controls */
191#ifdef HAVE_SW_TONE_CONTROLS 198#ifdef HAVE_SW_TONE_CONTROLS
192static int prescale; 199static int prescale; /* A/V */
193static int bass; 200static int bass; /* A/V */
194static int treble; 201static int treble; /* A/V */
195/* Filter struct for software bass/treble controls */
196static struct eqfilter tone_filter;
197#endif 202#endif
198 203
199/* Settings applicable to audio codec only */ 204/* Settings applicable to audio codec only */
@@ -202,7 +207,6 @@ static int channels_mode;
202 long dsp_sw_gain; 207 long dsp_sw_gain;
203 long dsp_sw_cross; 208 long dsp_sw_cross;
204static bool dither_enabled; 209static bool dither_enabled;
205static bool eq_enabled IBSS_ATTR;
206static long eq_precut; 210static long eq_precut;
207static long track_gain; 211static long track_gain;
208static bool new_gain; 212static bool new_gain;
@@ -212,9 +216,8 @@ static long album_peak;
212static long replaygain; 216static long replaygain;
213static bool crossfeed_enabled; 217static bool crossfeed_enabled;
214 218
215#define audio_dsp (&dsp_conf[CODEC_IDX_AUDIO]) 219#define audio_dsp (dsp_conf[CODEC_IDX_AUDIO])
216#define voice_dsp (&dsp_conf[CODEC_IDX_VOICE]) 220#define voice_dsp (dsp_conf[CODEC_IDX_VOICE])
217static struct dsp_config *dsp IDATA_ATTR = audio_dsp;
218 221
219/* The internal format is 32-bit samples, non-interleaved, stereo. This 222/* The internal format is 32-bit samples, non-interleaved, stereo. This
220 * format is similar to the raw output from several codecs, so the amount 223 * format is similar to the raw output from several codecs, so the amount
@@ -224,14 +227,6 @@ static struct dsp_config *dsp IDATA_ATTR = audio_dsp;
224int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR; 227int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR;
225static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR; 228static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR;
226 229
227/* set a new dsp and return old one */
228static inline struct dsp_config * switch_dsp(struct dsp_config *_dsp)
229{
230 struct dsp_config * old_dsp = dsp;
231 dsp = _dsp;
232 return old_dsp;
233}
234
235#if 0 230#if 0
236/* Clip sample to arbitrary limits where range > 0 and min + range = max */ 231/* Clip sample to arbitrary limits where range > 0 and min + range = max */
237static inline long clip_sample(int32_t sample, int32_t min, int32_t range) 232static inline long clip_sample(int32_t sample, int32_t min, int32_t range)
@@ -263,8 +258,8 @@ int sound_get_pitch(void)
263void sound_set_pitch(int permille) 258void sound_set_pitch(int permille)
264{ 259{
265 pitch_ratio = permille; 260 pitch_ratio = permille;
266 261 dsp_configure(&audio_dsp, DSP_SWITCH_FREQUENCY,
267 dsp_configure(DSP_SWITCH_FREQUENCY, dsp->codec_frequency); 262 audio_dsp.codec_frequency);
268} 263}
269 264
270/* Convert count samples to the internal format, if needed. Updates src 265/* Convert count samples to the internal format, if needed. Updates src
@@ -386,7 +381,7 @@ static void sample_input_gt_native_ni_stereo(
386 * * dsp->stereo_mode (A/V) 381 * * dsp->stereo_mode (A/V)
387 * * dsp->sample_depth (A/V) 382 * * dsp->sample_depth (A/V)
388 */ 383 */
389static void sample_input_new_format(void) 384static void sample_input_new_format(struct dsp_config *dsp)
390{ 385{
391 static const sample_input_fn_type sample_input_functions[] = 386 static const sample_input_fn_type sample_input_functions[] =
392 { 387 {
@@ -462,7 +457,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
462 int ch; 457 int ch;
463 int16_t *d; 458 int16_t *d;
464 459
465 for (ch = 0; ch < dsp->data.num_channels; ch++) 460 for (ch = 0; ch < data->num_channels; ch++)
466 { 461 {
467 struct dither_data * const dither = &dither_data[ch]; 462 struct dither_data * const dither = &dither_data[ch];
468 int32_t *s = src[ch]; 463 int32_t *s = src[ch];
@@ -505,7 +500,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
505 } 500 }
506 } 501 }
507 502
508 if (dsp->data.num_channels == 2) 503 if (data->num_channels == 2)
509 return; 504 return;
510 505
511 /* Have to duplicate left samples into the right channel since 506 /* Have to duplicate left samples into the right channel since
@@ -530,7 +525,7 @@ static void sample_output_dithered(int count, struct dsp_data *data,
530 * * dsp->stereo_mode (A/V) 525 * * dsp->stereo_mode (A/V)
531 * * dither_enabled (A) 526 * * dither_enabled (A)
532 */ 527 */
533static void sample_output_new_format(void) 528static void sample_output_new_format(struct dsp_config *dsp)
534{ 529{
535 static const sample_output_fn_type sample_output_functions[] = 530 static const sample_output_fn_type sample_output_functions[] =
536 { 531 {
@@ -542,7 +537,7 @@ static void sample_output_new_format(void)
542 537
543 int out = dsp->data.num_channels - 1; 538 int out = dsp->data.num_channels - 1;
544 539
545 if (dsp == audio_dsp && dither_enabled) 540 if (dsp == &audio_dsp && dither_enabled)
546 out += 2; 541 out += 2;
547 542
548 dsp->output_samples = sample_output_functions[out]; 543 dsp->output_samples = sample_output_functions[out];
@@ -638,7 +633,7 @@ static int dsp_upsample(int count, struct dsp_data *data,
638} 633}
639#endif /* DSP_HAVE_ASM_RESAMPLING */ 634#endif /* DSP_HAVE_ASM_RESAMPLING */
640 635
641static void resampler_new_delta(void) 636static void resampler_new_delta(struct dsp_config *dsp)
642{ 637{
643 dsp->data.resample_data.delta = (unsigned long) 638 dsp->data.resample_data.delta = (unsigned long)
644 dsp->frequency * 65536LL / NATIVE_FREQUENCY; 639 dsp->frequency * 65536LL / NATIVE_FREQUENCY;
@@ -663,7 +658,7 @@ static void resampler_new_delta(void)
663 * done, to refer to the resampled data. Returns number of stereo samples 658 * done, to refer to the resampled data. Returns number of stereo samples
664 * for further processing. 659 * for further processing.
665 */ 660 */
666static inline int resample(int count, int32_t *src[]) 661static inline int resample(struct dsp_config *dsp, int count, int32_t *src[])
667{ 662{
668 int32_t *dst[2] = 663 int32_t *dst[2] =
669 { 664 {
@@ -679,12 +674,8 @@ static inline int resample(int count, int32_t *src[])
679 return count; 674 return count;
680} 675}
681 676
682static void dither_init(void) 677static void dither_init(struct dsp_config *dsp)
683{ 678{
684 /* Voice codec should not reset the audio codec's dither data */
685 if (dsp != audio_dsp)
686 return;
687
688 memset(dither_data, 0, sizeof (dither_data)); 679 memset(dither_data, 0, sizeof (dither_data));
689 dither_bias = (1L << (dsp->frac_bits - NATIVE_DEPTH)); 680 dither_bias = (1L << (dsp->frac_bits - NATIVE_DEPTH));
690 dither_mask = (1L << (dsp->frac_bits + 1 - NATIVE_DEPTH)) - 1; 681 dither_mask = (1L << (dsp->frac_bits + 1 - NATIVE_DEPTH)) - 1;
@@ -692,11 +683,9 @@ static void dither_init(void)
692 683
693void dsp_dither_enable(bool enable) 684void dsp_dither_enable(bool enable)
694{ 685{
695 /* Be sure audio dsp is current to set correct function */ 686 struct dsp_config *dsp = &audio_dsp;
696 struct dsp_config *old_dsp = switch_dsp(audio_dsp);
697 dither_enabled = enable; 687 dither_enabled = enable;
698 sample_output_new_format(); 688 sample_output_new_format(dsp);
699 switch_dsp(old_dsp);
700} 689}
701 690
702/* Applies crossfeed to the stereo signal in src. 691/* Applies crossfeed to the stereo signal in src.
@@ -762,9 +751,8 @@ static void apply_crossfeed(int count, int32_t *buf[])
762void dsp_set_crossfeed(bool enable) 751void dsp_set_crossfeed(bool enable)
763{ 752{
764 crossfeed_enabled = enable; 753 crossfeed_enabled = enable;
765 audio_dsp->apply_crossfeed = 754 audio_dsp.apply_crossfeed = (enable && audio_dsp.data.num_channels > 1)
766 (enable && audio_dsp->data.num_channels > 1) 755 ? apply_crossfeed : NULL;
767 ? apply_crossfeed : NULL;
768} 756}
769 757
770void dsp_set_crossfeed_direct_gain(int gain) 758void dsp_set_crossfeed_direct_gain(int gain)
@@ -830,12 +818,12 @@ static void set_gain(struct dsp_config *dsp)
830 dsp->data.gain = DEFAULT_GAIN; 818 dsp->data.gain = DEFAULT_GAIN;
831 819
832 /* Replay gain not relevant to voice */ 820 /* Replay gain not relevant to voice */
833 if (dsp == audio_dsp && replaygain) 821 if (dsp == &audio_dsp && replaygain)
834 { 822 {
835 dsp->data.gain = replaygain; 823 dsp->data.gain = replaygain;
836 } 824 }
837 825
838 if (eq_enabled && eq_precut) 826 if (dsp->eq_process && eq_precut)
839 { 827 {
840 dsp->data.gain = 828 dsp->data.gain =
841 (long) (((int64_t) dsp->data.gain * eq_precut) >> 24); 829 (long) (((int64_t) dsp->data.gain * eq_precut) >> 24);
@@ -854,16 +842,6 @@ static void set_gain(struct dsp_config *dsp)
854} 842}
855 843
856/** 844/**
857 * Use to enable the equalizer.
858 *
859 * @param enable true to enable the equalizer
860 */
861void dsp_set_eq(bool enable)
862{
863 eq_enabled = enable;
864}
865
866/**
867 * Update the amount to cut the audio before applying the equalizer. 845 * Update the amount to cut the audio before applying the equalizer.
868 * 846 *
869 * @param precut to apply in decibels (multiplied by 10) 847 * @param precut to apply in decibels (multiplied by 10)
@@ -871,8 +849,7 @@ void dsp_set_eq(bool enable)
871void dsp_set_eq_precut(int precut) 849void dsp_set_eq_precut(int precut)
872{ 850{
873 eq_precut = get_replaygain_int(precut * -10); 851 eq_precut = get_replaygain_int(precut * -10);
874 set_gain(audio_dsp); 852 set_gain(&audio_dsp);
875 set_gain(voice_dsp); /* For EQ precut */
876} 853}
877 854
878/** 855/**
@@ -929,7 +906,7 @@ static void eq_process(int count, int32_t *buf[])
929 EQ_PEAK_SHIFT, /* peaking */ 906 EQ_PEAK_SHIFT, /* peaking */
930 EQ_SHELF_SHIFT, /* high shelf */ 907 EQ_SHELF_SHIFT, /* high shelf */
931 }; 908 };
932 unsigned int channels = dsp->data.num_channels; 909 unsigned int channels = audio_dsp.data.num_channels;
933 int i; 910 int i;
934 911
935 /* filter configuration currently is 1 low shelf filter, 3 band peaking 912 /* filter configuration currently is 1 low shelf filter, 3 band peaking
@@ -944,6 +921,17 @@ static void eq_process(int count, int32_t *buf[])
944 } 921 }
945} 922}
946 923
924/**
925 * Use to enable the equalizer.
926 *
927 * @param enable true to enable the equalizer
928 */
929void dsp_set_eq(bool enable)
930{
931 audio_dsp.eq_process = enable ? eq_process : NULL;
932 set_gain(&audio_dsp);
933}
934
947void dsp_set_stereo_width(int value) 935void dsp_set_stereo_width(int value)
948{ 936{
949 long width, straight, cross; 937 long width, straight, cross;
@@ -966,50 +954,6 @@ void dsp_set_stereo_width(int value)
966 dsp_sw_cross = cross << 8; 954 dsp_sw_cross = cross << 8;
967} 955}
968 956
969#if CONFIG_CODEC == SWCODEC
970
971#ifdef HAVE_SW_TONE_CONTROLS
972static void set_tone_controls(void)
973{
974 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
975 0xffffffff/NATIVE_FREQUENCY*3500,
976 bass, treble, -prescale, tone_filter.coefs);
977}
978#endif
979
980/* Hook back from firmware/ part of audio, which can't/shouldn't call apps/
981 * code directly.
982 */
983int dsp_callback(int msg, intptr_t param)
984{
985 switch (msg) {
986#ifdef HAVE_SW_TONE_CONTROLS
987 case DSP_CALLBACK_SET_PRESCALE:
988 prescale = param;
989 set_tone_controls();
990 break;
991 /* prescaler is always set after calling any of these, so we wait with
992 * calculating coefs until the above case is hit.
993 */
994 case DSP_CALLBACK_SET_BASS:
995 bass = param;
996 break;
997 case DSP_CALLBACK_SET_TREBLE:
998 treble = param;
999#endif
1000 case DSP_CALLBACK_SET_CHANNEL_CONFIG:
1001 dsp_set_channel_config(param);
1002 break;
1003 case DSP_CALLBACK_SET_STEREO_WIDTH:
1004 dsp_set_stereo_width(param);
1005 break;
1006 default:
1007 break;
1008 }
1009 return 0;
1010}
1011#endif
1012
1013/** 957/**
1014 * Implements the different channel configurations and stereo width. 958 * Implements the different channel configurations and stereo width.
1015 */ 959 */
@@ -1098,13 +1042,63 @@ void dsp_set_channel_config(int value)
1098 }; 1042 };
1099 1043
1100 if ((unsigned)value >= ARRAYLEN(channels_process_functions) || 1044 if ((unsigned)value >= ARRAYLEN(channels_process_functions) ||
1101 audio_dsp->stereo_mode == STEREO_MONO) 1045 audio_dsp.stereo_mode == STEREO_MONO)
1046 {
1102 value = SOUND_CHAN_STEREO; 1047 value = SOUND_CHAN_STEREO;
1048 }
1103 1049
1104 /* This doesn't apply to voice */ 1050 /* This doesn't apply to voice */
1105 channels_mode = value; 1051 channels_mode = value;
1106 audio_dsp->channels_process = channels_process_functions[value]; 1052 audio_dsp.channels_process = channels_process_functions[value];
1053}
1054
1055#if CONFIG_CODEC == SWCODEC
1056
1057#ifdef HAVE_SW_TONE_CONTROLS
1058static void set_tone_controls(void)
1059{
1060 filter_bishelf_coefs(0xffffffff/NATIVE_FREQUENCY*200,
1061 0xffffffff/NATIVE_FREQUENCY*3500,
1062 bass, treble, -prescale,
1063 audio_dsp.tone_filter.coefs);
1064 /* Sync the voice dsp coefficients */
1065 memcpy(&voice_dsp.tone_filter.coefs, audio_dsp.tone_filter.coefs,
1066 sizeof (voice_dsp.tone_filter.coefs));
1107} 1067}
1068#endif
1069
1070/* Hook back from firmware/ part of audio, which can't/shouldn't call apps/
1071 * code directly.
1072 */
1073int dsp_callback(int msg, intptr_t param)
1074{
1075 switch (msg) {
1076#ifdef HAVE_SW_TONE_CONTROLS
1077 case DSP_CALLBACK_SET_PRESCALE:
1078 prescale = param;
1079 set_tone_controls();
1080 break;
1081 /* prescaler is always set after calling any of these, so we wait with
1082 * calculating coefs until the above case is hit.
1083 */
1084 case DSP_CALLBACK_SET_BASS:
1085 bass = param;
1086 break;
1087 case DSP_CALLBACK_SET_TREBLE:
1088 treble = param;
1089#endif
1090 case DSP_CALLBACK_SET_CHANNEL_CONFIG:
1091 dsp_set_channel_config(param);
1092 break;
1093 case DSP_CALLBACK_SET_STEREO_WIDTH:
1094 dsp_set_stereo_width(param);
1095 break;
1096 default:
1097 break;
1098 }
1099 return 0;
1100}
1101#endif
1108 1102
1109/* Process and convert src audio to dst based on the DSP configuration, 1103/* Process and convert src audio to dst based on the DSP configuration,
1110 * reading count number of audio samples. dst is assumed to be large 1104 * reading count number of audio samples. dst is assumed to be large
@@ -1114,7 +1108,7 @@ void dsp_set_channel_config(int value)
1114 * non-interleaved stereo, it contains two pointers, one for each audio 1108 * non-interleaved stereo, it contains two pointers, one for each audio
1115 * channel. Returns number of bytes written to dst. 1109 * channel. Returns number of bytes written to dst.
1116 */ 1110 */
1117int dsp_process(char *dst, const char *src[], int count) 1111int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1118{ 1112{
1119 int32_t *tmp[2]; 1113 int32_t *tmp[2];
1120 int written = 0; 1114 int written = 0;
@@ -1142,25 +1136,19 @@ int dsp_process(char *dst, const char *src[], int count)
1142 if (dsp->apply_gain) 1136 if (dsp->apply_gain)
1143 dsp->apply_gain(samples, &dsp->data, tmp); 1137 dsp->apply_gain(samples, &dsp->data, tmp);
1144 1138
1145 if (dsp->resample && (samples = resample(samples, tmp)) <= 0) 1139 if (dsp->resample && (samples = resample(dsp, samples, tmp)) <= 0)
1146 break; /* I'm pretty sure we're downsampling here */ 1140 break; /* I'm pretty sure we're downsampling here */
1147 1141
1148 if (dsp->apply_crossfeed) 1142 if (dsp->apply_crossfeed)
1149 dsp->apply_crossfeed(samples, tmp); 1143 dsp->apply_crossfeed(samples, tmp);
1150 1144
1151 /* TODO: EQ and tone controls need separate structs for audio and voice 1145 if (dsp->eq_process)
1152 * DSP processing thanks to filter history. isn't really audible now, but 1146 dsp->eq_process(samples, tmp);
1153 * might be the day we start handling voice more delicately. Planned
1154 * changes may well run all relevent channels through the same EQ so
1155 * perhaps not.
1156 */
1157 if (eq_enabled)
1158 eq_process(samples, tmp);
1159 1147
1160#ifdef HAVE_SW_TONE_CONTROLS 1148#ifdef HAVE_SW_TONE_CONTROLS
1161 if ((bass | treble) != 0) 1149 if ((bass | treble) != 0)
1162 eq_filter(tmp, &tone_filter, samples, dsp->data.num_channels, 1150 eq_filter(tmp, &dsp->tone_filter, samples,
1163 FILTER_BISHELF_SHIFT); 1151 dsp->data.num_channels, FILTER_BISHELF_SHIFT);
1164#endif 1152#endif
1165 1153
1166 if (dsp->channels_process) 1154 if (dsp->channels_process)
@@ -1187,7 +1175,7 @@ int dsp_process(char *dst, const char *src[], int count)
1187 * of the resampler). 1175 * of the resampler).
1188 */ 1176 */
1189/* dsp_input_size MUST be called afterwards */ 1177/* dsp_input_size MUST be called afterwards */
1190int dsp_output_count(int count) 1178int dsp_output_count(struct dsp_config *dsp, int count)
1191{ 1179{
1192 if (dsp->resample) 1180 if (dsp->resample)
1193 { 1181 {
@@ -1209,7 +1197,7 @@ int dsp_output_count(int count)
1209/* Given count output samples, calculate number of input samples 1197/* Given count output samples, calculate number of input samples
1210 * that would be consumed in order to fill the output buffer. 1198 * that would be consumed in order to fill the output buffer.
1211 */ 1199 */
1212int dsp_input_count(int count) 1200int dsp_input_count(struct dsp_config *dsp, int count)
1213{ 1201{
1214 /* count is now the number of resampled input samples. Convert to 1202 /* count is now the number of resampled input samples. Convert to
1215 original input samples. */ 1203 original input samples. */
@@ -1225,41 +1213,37 @@ int dsp_input_count(int count)
1225 return count; 1213 return count;
1226} 1214}
1227 1215
1228int dsp_stereo_mode(void)
1229{
1230 return dsp->stereo_mode;
1231}
1232
1233static void dsp_set_gain_var(long *var, long value) 1216static void dsp_set_gain_var(long *var, long value)
1234{ 1217{
1235 /* Voice shouldn't mess with these */ 1218 *var = value;
1236 if (dsp == audio_dsp) 1219 new_gain = true;
1237 {
1238 *var = value;
1239 new_gain = true;
1240 }
1241} 1220}
1242 1221
1243static void dsp_update_functions(void) 1222static void dsp_update_functions(struct dsp_config *dsp)
1244{ 1223{
1245 sample_input_new_format(); 1224 sample_input_new_format(dsp);
1246 sample_output_new_format(); 1225 sample_output_new_format(dsp);
1247 if (dsp == audio_dsp) 1226 if (dsp == &audio_dsp)
1248 dsp_set_crossfeed(crossfeed_enabled); 1227 dsp_set_crossfeed(crossfeed_enabled);
1249} 1228}
1250 1229
1251bool dsp_configure(int setting, intptr_t value) 1230intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
1252{ 1231{
1253 switch (setting) 1232 switch (setting)
1254 { 1233 {
1255 case DSP_SWITCH_CODEC: 1234 case DSP_MYDSP:
1256 if ((uintptr_t)value <= 1) 1235 switch (value)
1257 switch_dsp(&dsp_conf[value]); 1236 {
1258 break; 1237 case CODEC_IDX_AUDIO:
1238 return (intptr_t)&audio_dsp;
1239 case CODEC_IDX_VOICE:
1240 return (intptr_t)&voice_dsp;
1241 default:
1242 return (intptr_t)NULL;
1243 }
1259 1244
1260 case DSP_SET_FREQUENCY: 1245 case DSP_SET_FREQUENCY:
1261 memset(&dsp->data.resample_data, 0, 1246 memset(&dsp->data.resample_data, 0, sizeof (dsp->data.resample_data));
1262 sizeof (dsp->data.resample_data));
1263 /* Fall through!!! */ 1247 /* Fall through!!! */
1264 case DSP_SWITCH_FREQUENCY: 1248 case DSP_SWITCH_FREQUENCY:
1265 dsp->codec_frequency = (value == 0) ? NATIVE_FREQUENCY : value; 1249 dsp->codec_frequency = (value == 0) ? NATIVE_FREQUENCY : value;
@@ -1267,12 +1251,12 @@ bool dsp_configure(int setting, intptr_t value)
1267 if we're called from the main audio thread. Voice UI thread should 1251 if we're called from the main audio thread. Voice UI thread should
1268 not need this feature. 1252 not need this feature.
1269 */ 1253 */
1270 if (dsp == audio_dsp) 1254 if (dsp == &audio_dsp)
1271 dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000; 1255 dsp->frequency = pitch_ratio * dsp->codec_frequency / 1000;
1272 else 1256 else
1273 dsp->frequency = dsp->codec_frequency; 1257 dsp->frequency = dsp->codec_frequency;
1274 1258
1275 resampler_new_delta(); 1259 resampler_new_delta(dsp);
1276 break; 1260 break;
1277 1261
1278 case DSP_SET_SAMPLE_DEPTH: 1262 case DSP_SET_SAMPLE_DEPTH:
@@ -1294,14 +1278,14 @@ bool dsp_configure(int setting, intptr_t value)
1294 } 1278 }
1295 1279
1296 dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH; 1280 dsp->data.output_scale = dsp->frac_bits + 1 - NATIVE_DEPTH;
1297 sample_input_new_format(); 1281 sample_input_new_format(dsp);
1298 dither_init(); 1282 dither_init(dsp);
1299 break; 1283 break;
1300 1284
1301 case DSP_SET_STEREO_MODE: 1285 case DSP_SET_STEREO_MODE:
1302 dsp->stereo_mode = value; 1286 dsp->stereo_mode = value;
1303 dsp->data.num_channels = value == STEREO_MONO ? 1 : 2; 1287 dsp->data.num_channels = value == STEREO_MONO ? 1 : 2;
1304 dsp_update_functions(); 1288 dsp_update_functions(dsp);
1305 break; 1289 break;
1306 1290
1307 case DSP_RESET: 1291 case DSP_RESET:
@@ -1315,7 +1299,7 @@ bool dsp_configure(int setting, intptr_t value)
1315 dsp->data.clip_min = -((1 << WORD_FRACBITS)); 1299 dsp->data.clip_min = -((1 << WORD_FRACBITS));
1316 dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY; 1300 dsp->codec_frequency = dsp->frequency = NATIVE_FREQUENCY;
1317 1301
1318 if (dsp == audio_dsp) 1302 if (dsp == &audio_dsp)
1319 { 1303 {
1320 track_gain = 0; 1304 track_gain = 0;
1321 album_gain = 0; 1305 album_gain = 0;
@@ -1324,31 +1308,35 @@ bool dsp_configure(int setting, intptr_t value)
1324 new_gain = true; 1308 new_gain = true;
1325 } 1309 }
1326 1310
1327 dsp_update_functions(); 1311 dsp_update_functions(dsp);
1328 resampler_new_delta(); 1312 resampler_new_delta(dsp);
1329 break; 1313 break;
1330 1314
1331 case DSP_FLUSH: 1315 case DSP_FLUSH:
1332 memset(&dsp->data.resample_data, 0, 1316 memset(&dsp->data.resample_data, 0,
1333 sizeof (dsp->data.resample_data)); 1317 sizeof (dsp->data.resample_data));
1334 resampler_new_delta(); 1318 resampler_new_delta(dsp);
1335 dither_init(); 1319 dither_init(dsp);
1336 break; 1320 break;
1337 1321
1338 case DSP_SET_TRACK_GAIN: 1322 case DSP_SET_TRACK_GAIN:
1339 dsp_set_gain_var(&track_gain, value); 1323 if (dsp == &audio_dsp)
1324 dsp_set_gain_var(&track_gain, value);
1340 break; 1325 break;
1341 1326
1342 case DSP_SET_ALBUM_GAIN: 1327 case DSP_SET_ALBUM_GAIN:
1343 dsp_set_gain_var(&album_gain, value); 1328 if (dsp == &audio_dsp)
1329 dsp_set_gain_var(&album_gain, value);
1344 break; 1330 break;
1345 1331
1346 case DSP_SET_TRACK_PEAK: 1332 case DSP_SET_TRACK_PEAK:
1347 dsp_set_gain_var(&track_peak, value); 1333 if (dsp == &audio_dsp)
1334 dsp_set_gain_var(&track_peak, value);
1348 break; 1335 break;
1349 1336
1350 case DSP_SET_ALBUM_PEAK: 1337 case DSP_SET_ALBUM_PEAK:
1351 dsp_set_gain_var(&album_peak, value); 1338 if (dsp == &audio_dsp)
1339 dsp_set_gain_var(&album_peak, value);
1352 break; 1340 break;
1353 1341
1354 default: 1342 default:
@@ -1404,5 +1392,5 @@ void dsp_set_replaygain(void)
1404 1392
1405 /* Store in S8.23 format to simplify calculations. */ 1393 /* Store in S8.23 format to simplify calculations. */
1406 replaygain = gain; 1394 replaygain = gain;
1407 set_gain(audio_dsp); 1395 set_gain(&audio_dsp);
1408} 1396}
diff --git a/apps/dsp.h b/apps/dsp.h
index 838dc617ee..799d023aee 100644
--- a/apps/dsp.h
+++ b/apps/dsp.h
@@ -34,8 +34,14 @@ enum
34 34
35enum 35enum
36{ 36{
37 CODEC_IDX_AUDIO = 0,
38 CODEC_IDX_VOICE,
39};
40
41enum
42{
37 CODEC_SET_FILEBUF_WATERMARK = 1, 43 CODEC_SET_FILEBUF_WATERMARK = 1,
38 DSP_SWITCH_CODEC, 44 DSP_MYDSP,
39 DSP_SET_FREQUENCY, 45 DSP_SET_FREQUENCY,
40 DSP_SWITCH_FREQUENCY, 46 DSP_SWITCH_FREQUENCY,
41 DSP_SET_SAMPLE_DEPTH, 47 DSP_SET_SAMPLE_DEPTH,
@@ -201,23 +207,25 @@ enum {
201 207
202#define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y)) 208#define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y))
203 209
204int dsp_process(char *dest, const char *src[], int count); 210struct dsp_config;
205int dsp_input_count(int count); 211
206int dsp_output_count(int count); 212int dsp_process(struct dsp_config *dsp, char *dest,
207int dsp_stereo_mode(void); 213 const char *src[], int count);
208bool dsp_configure(int setting, intptr_t value); 214int dsp_input_count(struct dsp_config *dsp, int count);
215int dsp_output_count(struct dsp_config *dsp, int count);
216intptr_t dsp_configure(struct dsp_config *dsp, int setting,
217 intptr_t value);
209void dsp_set_replaygain(void); 218void dsp_set_replaygain(void);
210void dsp_set_crossfeed(bool enable); 219void dsp_set_crossfeed(bool enable);
211void dsp_set_crossfeed_direct_gain(int gain); 220void dsp_set_crossfeed_direct_gain(int gain);
212void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff); 221void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain,
222 long cutoff);
213void dsp_set_eq(bool enable); 223void dsp_set_eq(bool enable);
214void dsp_set_eq_precut(int precut); 224void dsp_set_eq_precut(int precut);
215void dsp_set_eq_coefs(int band); 225void dsp_set_eq_coefs(int band);
216void sound_set_pitch(int r); 226void sound_set_pitch(int r);
217int sound_get_pitch(void); 227int sound_get_pitch(void);
218int dsp_callback(int msg, intptr_t param); 228int dsp_callback(int msg, intptr_t param);
219void dsp_set_channel_config(int value);
220void dsp_set_stereo_width(int value);
221void dsp_dither_enable(bool enable); 229void dsp_dither_enable(bool enable);
222 230
223#endif 231#endif
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 49333d108b..4ed2973dbb 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -34,6 +34,7 @@
34#include "buffer.h" 34#include "buffer.h"
35#include "settings.h" 35#include "settings.h"
36#include "audio.h" 36#include "audio.h"
37#include "voice_thread.h"
37#include "dsp.h" 38#include "dsp.h"
38#include "thread.h" 39#include "thread.h"
39 40
@@ -251,15 +252,21 @@ static inline void pcmbuf_add_chunk(void)
251#ifdef HAVE_PRIORITY_SCHEDULING 252#ifdef HAVE_PRIORITY_SCHEDULING
252static void boost_codec_thread(bool boost) 253static void boost_codec_thread(bool boost)
253{ 254{
255 /* Keep voice and codec threads at the same priority or else voice
256 * will starve if the codec thread's priority is boosted. */
254 if (boost) 257 if (boost)
255 { 258 {
256 if (codec_thread_priority == 0) 259 if (codec_thread_priority == 0)
260 {
257 codec_thread_priority = thread_set_priority( 261 codec_thread_priority = thread_set_priority(
258 codec_thread_p, PRIORITY_REALTIME); 262 codec_thread_p, PRIORITY_REALTIME);
263 voice_thread_set_priority(PRIORITY_REALTIME);
264 }
259 } 265 }
260 else if (codec_thread_priority != 0) 266 else if (codec_thread_priority != 0)
261 { 267 {
262 thread_set_priority(codec_thread_p, codec_thread_priority); 268 thread_set_priority(codec_thread_p, codec_thread_priority);
269 voice_thread_set_priority(codec_thread_priority);
263 codec_thread_priority = 0; 270 codec_thread_priority = 0;
264 } 271 }
265} 272}
@@ -717,6 +724,7 @@ static size_t crossfade_mix(const char *buf, size_t length)
717 crossfade_chunk = crossfade_chunk->link; 724 crossfade_chunk = crossfade_chunk->link;
718 if (!crossfade_chunk) 725 if (!crossfade_chunk)
719 return length; 726 return length;
727
720 output_buf = (int16_t *)crossfade_chunk->addr; 728 output_buf = (int16_t *)crossfade_chunk->addr;
721 chunk_end = SKIPBYTES(output_buf, crossfade_chunk->size); 729 chunk_end = SKIPBYTES(output_buf, crossfade_chunk->size);
722 } 730 }
@@ -875,15 +883,18 @@ void* pcmbuf_request_buffer(int *count)
875 } 883 }
876} 884}
877 885
878void* pcmbuf_request_voice_buffer(int *count, bool mix) 886void * pcmbuf_request_voice_buffer(int *count)
879{ 887{
880 if (mix) 888 /* A get-it-to-work-for-now hack (audio status could change by
889 completion) */
890 if (audio_status() & AUDIO_STATUS_PLAY)
881 { 891 {
882 if (pcmbuf_read == NULL) 892 if (pcmbuf_read == NULL)
883 { 893 {
884 return NULL; 894 return NULL;
885 } 895 }
886 else if (pcmbuf_mix_chunk || pcmbuf_read->link) 896 else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
897 (pcmbuf_mix_chunk || pcmbuf_read->link))
887 { 898 {
888 *count = MIN(*count, PCMBUF_MIX_CHUNK/4); 899 *count = MIN(*count, PCMBUF_MIX_CHUNK/4);
889 return voicebuf; 900 return voicebuf;
@@ -894,7 +905,9 @@ void* pcmbuf_request_voice_buffer(int *count, bool mix)
894 } 905 }
895 } 906 }
896 else 907 else
908 {
897 return pcmbuf_request_buffer(count); 909 return pcmbuf_request_buffer(count);
910 }
898} 911}
899 912
900bool pcmbuf_is_crossfade_active(void) 913bool pcmbuf_is_crossfade_active(void)
@@ -1030,8 +1043,15 @@ int pcmbuf_mix_free(void)
1030 return 100; 1043 return 100;
1031} 1044}
1032 1045
1033void pcmbuf_mix_voice(int count) 1046void pcmbuf_write_voice_complete(int count)
1034{ 1047{
1048 /* A get-it-to-work-for-now hack (audio status could have changed) */
1049 if (!(audio_status() & AUDIO_STATUS_PLAY))
1050 {
1051 pcmbuf_write_complete(count);
1052 return;
1053 }
1054
1035 int16_t *ibuf = (int16_t *)voicebuf; 1055 int16_t *ibuf = (int16_t *)voicebuf;
1036 int16_t *obuf; 1056 int16_t *obuf;
1037 size_t chunk_samples; 1057 size_t chunk_samples;
@@ -1042,6 +1062,7 @@ void pcmbuf_mix_voice(int count)
1042 /* Start 1/8s into the next chunk */ 1062 /* Start 1/8s into the next chunk */
1043 pcmbuf_mix_sample = NATIVE_FREQUENCY * 4 / 16; 1063 pcmbuf_mix_sample = NATIVE_FREQUENCY * 4 / 16;
1044 } 1064 }
1065
1045 if (!pcmbuf_mix_chunk) 1066 if (!pcmbuf_mix_chunk)
1046 return; 1067 return;
1047 1068
@@ -1053,6 +1074,7 @@ void pcmbuf_mix_voice(int count)
1053 while (count-- > 0) 1074 while (count-- > 0)
1054 { 1075 {
1055 int32_t sample = *ibuf++; 1076 int32_t sample = *ibuf++;
1077
1056 if (pcmbuf_mix_sample >= chunk_samples) 1078 if (pcmbuf_mix_sample >= chunk_samples)
1057 { 1079 {
1058 pcmbuf_mix_chunk = pcmbuf_mix_chunk->link; 1080 pcmbuf_mix_chunk = pcmbuf_mix_chunk->link;
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index bb7da52644..06362452c0 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -67,16 +67,16 @@ void pcmbuf_set_position_callback(void (*callback)(size_t size));
67size_t pcmbuf_free(void); 67size_t pcmbuf_free(void);
68unsigned int pcmbuf_get_latency(void); 68unsigned int pcmbuf_get_latency(void);
69void pcmbuf_set_low_latency(bool state); 69void pcmbuf_set_low_latency(bool state);
70void * pcmbuf_request_buffer(int *count);
70void pcmbuf_write_complete(int count); 71void pcmbuf_write_complete(int count);
71void* pcmbuf_request_buffer(int *count); 72void * pcmbuf_request_voice_buffer(int *count);
72void* pcmbuf_request_voice_buffer(int *count, bool mix); 73void pcmbuf_write_voice_complete(int count);
73bool pcmbuf_is_crossfade_enabled(void); 74bool pcmbuf_is_crossfade_enabled(void);
74void pcmbuf_crossfade_enable(bool on_off); 75void pcmbuf_crossfade_enable(bool on_off);
75void pcmbuf_crossfade_enable_finished(void); 76void pcmbuf_crossfade_enable_finished(void);
76int pcmbuf_usage(void); 77int pcmbuf_usage(void);
77int pcmbuf_mix_free(void); 78int pcmbuf_mix_free(void);
78void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); 79void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
79void pcmbuf_mix_voice(int count);
80 80
81int pcmbuf_used_descs(void); 81int pcmbuf_used_descs(void);
82int pcmbuf_descs(void); 82int pcmbuf_descs(void);
diff --git a/apps/playback.c b/apps/playback.c
index 90245e86cb..eac5307acc 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -47,6 +47,7 @@
47#include "codecs.h" 47#include "codecs.h"
48#include "audio.h" 48#include "audio.h"
49#include "buffering.h" 49#include "buffering.h"
50#include "voice_thread.h"
50#include "mp3_playback.h" 51#include "mp3_playback.h"
51#include "usb.h" 52#include "usb.h"
52#include "status.h" 53#include "status.h"
@@ -88,7 +89,6 @@
88 89
89#define PLAYBACK_VOICE 90#define PLAYBACK_VOICE
90 91
91
92/* default point to start buffer refill */ 92/* default point to start buffer refill */
93#define AUDIO_DEFAULT_WATERMARK (1024*512) 93#define AUDIO_DEFAULT_WATERMARK (1024*512)
94/* amount of guess-space to allow for codecs that must hunt and peck 94/* amount of guess-space to allow for codecs that must hunt and peck
@@ -142,9 +142,6 @@ enum {
142 Q_CODEC_REQUEST_COMPLETE, 142 Q_CODEC_REQUEST_COMPLETE,
143 Q_CODEC_REQUEST_FAILED, 143 Q_CODEC_REQUEST_FAILED,
144 144
145 Q_VOICE_PLAY,
146 Q_VOICE_STOP,
147
148 Q_CODEC_LOAD, 145 Q_CODEC_LOAD,
149 Q_CODEC_LOAD_DISK, 146 Q_CODEC_LOAD_DISK,
150 147
@@ -175,10 +172,6 @@ enum {
175#define CODEC_IRAM_SIZE ((size_t)0xc000) 172#define CODEC_IRAM_SIZE ((size_t)0xc000)
176#endif 173#endif
177 174
178#ifndef IBSS_ATTR_VOICE_STACK
179#define IBSS_ATTR_VOICE_STACK IBSS_ATTR
180#endif
181
182bool audio_is_initialized = false; 175bool audio_is_initialized = false;
183 176
184/* Variables are commented with the threads that use them: * 177/* Variables are commented with the threads that use them: *
@@ -267,7 +260,6 @@ void (*track_unbuffer_callback)(struct mp3entry *id3) = NULL;
267static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */ 260static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */
268 261
269/* Multiple threads */ 262/* Multiple threads */
270static void set_current_codec(int codec_idx);
271/* Set the watermark to trigger buffer fill (A/C) FIXME */ 263/* Set the watermark to trigger buffer fill (A/C) FIXME */
272static void set_filebuf_watermark(int seconds, size_t max); 264static void set_filebuf_watermark(int seconds, size_t max);
273 265
@@ -291,68 +283,6 @@ IBSS_ATTR;
291static const char codec_thread_name[] = "codec"; 283static const char codec_thread_name[] = "codec";
292struct thread_entry *codec_thread_p; /* For modifying thread priority later. */ 284struct thread_entry *codec_thread_p; /* For modifying thread priority later. */
293 285
294static volatile int current_codec IDATA_ATTR; /* Current codec (normal/voice) */
295
296/* Voice thread */
297#ifdef PLAYBACK_VOICE
298
299extern struct codec_api ci_voice;
300
301static struct thread_entry *voice_thread_p = NULL;
302static struct event_queue voice_queue NOCACHEBSS_ATTR;
303static long voice_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
304IBSS_ATTR_VOICE_STACK;
305static const char voice_thread_name[] = "voice codec";
306
307/* Voice codec swapping control */
308extern unsigned char codecbuf[]; /* DRAM codec swap buffer */
309
310#ifdef SIMULATOR
311/* IRAM codec swap buffer for sim*/
312static unsigned char sim_iram[CODEC_IRAM_SIZE];
313#undef CODEC_IRAM_ORIGIN
314#define CODEC_IRAM_ORIGIN sim_iram
315#endif
316
317/* iram_buf and dram_buf are either both NULL or both non-NULL */
318/* Pointer to IRAM buffer for codec swapping */
319static unsigned char *iram_buf = NULL;
320/* Pointer to DRAM buffer for codec swapping */
321static unsigned char *dram_buf = NULL;
322/* Parity of swap_codec calls - needed because one codec swapping itself in
323 automatically swaps in the other and the swap when unlocking should not
324 happen if the parity is even.
325 */
326static bool swap_codec_parity NOCACHEBSS_ATTR = false; /* true=odd, false=even */
327/* Locking to control which codec (normal/voice) is running */
328static struct semaphore sem_codecthread NOCACHEBSS_ATTR;
329static struct event event_codecthread NOCACHEBSS_ATTR;
330
331/* Voice state */
332static volatile bool voice_thread_start = false; /* Triggers voice playback (A/V) */
333static volatile bool voice_is_playing NOCACHEBSS_ATTR = false; /* Is voice currently playing? (V) */
334static volatile bool voice_codec_loaded NOCACHEBSS_ATTR = false; /* Is voice codec loaded (V/A-) */
335static unsigned char *voicebuf = NULL;
336static size_t voice_remaining = 0;
337
338#ifdef IRAM_STEAL
339/* Voice IRAM has been stolen for other use */
340static bool voice_iram_stolen = false;
341#endif
342
343static void (*voice_getmore)(unsigned char** start, size_t* size) = NULL;
344
345struct voice_info {
346 void (*callback)(unsigned char **start, size_t* size);
347 size_t size;
348 unsigned char *buf;
349};
350static void voice_thread(void);
351static void voice_stop(void);
352
353#endif /* PLAYBACK_VOICE */
354
355
356/* --- Helper functions --- */ 286/* --- Helper functions --- */
357 287
358static struct mp3entry *bufgetid3(int handle_id) 288static struct mp3entry *bufgetid3(int handle_id)
@@ -415,71 +345,33 @@ static bool clear_track_info(struct track_info *track)
415 345
416/* --- External interfaces --- */ 346/* --- External interfaces --- */
417 347
418void mp3_play_data(const unsigned char* start, int size,
419 void (*get_more)(unsigned char** start, size_t* size))
420{
421#ifdef PLAYBACK_VOICE
422 static struct voice_info voice_clip;
423 voice_clip.callback = get_more;
424 voice_clip.buf = (unsigned char*)start;
425 voice_clip.size = size;
426 LOGFQUEUE("mp3 > voice Q_VOICE_STOP");
427 queue_post(&voice_queue, Q_VOICE_STOP, 0);
428 LOGFQUEUE("mp3 > voice Q_VOICE_PLAY");
429 queue_post(&voice_queue, Q_VOICE_PLAY, (intptr_t)&voice_clip);
430 voice_thread_start = true;
431 trigger_cpu_boost();
432#else
433 (void) start;
434 (void) size;
435 (void) get_more;
436#endif
437}
438
439void mp3_play_stop(void)
440{
441#ifdef PLAYBACK_VOICE
442 queue_remove_from_head(&voice_queue, Q_VOICE_STOP);
443 LOGFQUEUE("mp3 > voice Q_VOICE_STOP");
444 queue_post(&voice_queue, Q_VOICE_STOP, 1);
445#endif
446}
447
448void mp3_play_pause(bool play)
449{
450 /* a dummy */
451 (void)play;
452}
453
454bool mp3_is_playing(void)
455{
456#ifdef PLAYBACK_VOICE
457 return voice_is_playing;
458#else
459 return false;
460#endif
461}
462
463/* If voice could be swapped out - wait for it to return
464 * Used by buffer claming functions.
465 */
466static void wait_for_voice_swap_in(void)
467{
468#ifdef PLAYBACK_VOICE
469 if (NULL == iram_buf)
470 return;
471
472 event_wait(&event_codecthread, STATE_NONSIGNALED);
473#endif /* PLAYBACK_VOICE */
474}
475
476/* This sends a stop message and the audio thread will dump all it's 348/* This sends a stop message and the audio thread will dump all it's
477 subsequenct messages */ 349 subsequenct messages */
478static void audio_hard_stop(void) 350void audio_hard_stop(void)
479{ 351{
480 /* Stop playback */ 352 /* Stop playback */
481 LOGFQUEUE("audio >| audio Q_AUDIO_STOP: 1"); 353 LOGFQUEUE("audio >| audio Q_AUDIO_STOP: 1");
482 queue_send(&audio_queue, Q_AUDIO_STOP, 1); 354 queue_send(&audio_queue, Q_AUDIO_STOP, 1);
355#ifdef PLAYBACK_VOICE
356 voice_stop();
357#endif
358}
359
360bool audio_restore_playback(int type)
361{
362 switch (type)
363 {
364 case AUDIO_WANT_PLAYBACK:
365 if (buffer_state != BUFFER_STATE_INITIALIZED)
366 audio_reset_buffer();
367 return true;
368 case AUDIO_WANT_VOICE:
369 if (buffer_state == BUFFER_STATE_TRASHED)
370 audio_reset_buffer();
371 return true;
372 default:
373 return false;
374 }
483} 375}
484 376
485unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) 377unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
@@ -489,10 +381,6 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
489 if (audio_is_initialized) 381 if (audio_is_initialized)
490 { 382 {
491 audio_hard_stop(); 383 audio_hard_stop();
492 wait_for_voice_swap_in();
493#ifdef PLAYBACK_VOICE
494 voice_stop();
495#endif
496 } 384 }
497 /* else buffer_state will be BUFFER_STATE_TRASHED at this point */ 385 /* else buffer_state will be BUFFER_STATE_TRASHED at this point */
498 386
@@ -543,68 +431,15 @@ unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
543 return buf; 431 return buf;
544} 432}
545 433
546#ifdef IRAM_STEAL
547void audio_iram_steal(void)
548{
549 /* We need to stop audio playback in order to use codec IRAM */
550 audio_hard_stop();
551
552#ifdef PLAYBACK_VOICE
553 if (NULL != iram_buf)
554 {
555 /* Can't already be stolen */
556 if (voice_iram_stolen)
557 return;
558
559 /* Must wait for voice to be current again if it is swapped which
560 would cause the caller's buffer to get clobbered when voice locks
561 and runs - we'll wait for it to lock and yield again then make sure
562 the ride has come to a complete stop */
563 wait_for_voice_swap_in();
564 voice_stop();
565
566 /* Save voice IRAM but just memcpy - safe to do here since voice
567 is current and no audio codec is loaded */
568 memcpy(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE);
569 voice_iram_stolen = true;
570 }
571 else
572 {
573 /* Nothing much to do if no voice */
574 voice_iram_stolen = false;
575 }
576#endif
577}
578#endif /* IRAM_STEAL */
579
580#ifdef HAVE_RECORDING 434#ifdef HAVE_RECORDING
581unsigned char *audio_get_recording_buffer(size_t *buffer_size) 435unsigned char *audio_get_recording_buffer(size_t *buffer_size)
582{ 436{
583 /* Don't allow overwrite of voice swap area or we'll trash the 437 /* Stop audio, voice and obtain all available buffer space */
584 swapped-out voice codec but can use whole thing if none */
585 unsigned char *end;
586
587 /* Stop audio and voice. Wait for voice to swap in and be clear
588 of pending events to ensure trouble-free operation of encoders */
589 audio_hard_stop(); 438 audio_hard_stop();
590 wait_for_voice_swap_in();
591#ifdef PLAYBACK_VOICE
592 voice_stop();
593#endif
594 talk_buffer_steal(); 439 talk_buffer_steal();
595 440
596#ifdef PLAYBACK_VOICE 441 unsigned char *end = audiobufend;
597 /* If no dram_buf, swap space not used and recording gets more
598 memory. Codec swap areas will remain unaffected by the next init
599 since they're allocated at the end of the buffer and their sizes
600 don't change between calls */
601 end = dram_buf;
602 if (NULL == end)
603#endif /* PLAYBACK_VOICE */
604 end = audiobufend;
605
606 buffer_state = BUFFER_STATE_TRASHED; 442 buffer_state = BUFFER_STATE_TRASHED;
607
608 *buffer_size = end - audiobuf; 443 *buffer_size = end - audiobuf;
609 444
610 return (unsigned char *)audiobuf; 445 return (unsigned char *)audiobuf;
@@ -952,137 +787,6 @@ void audio_set_crossfade(int enable)
952} 787}
953 788
954/* --- Routines called from multiple threads --- */ 789/* --- Routines called from multiple threads --- */
955static void set_current_codec(int codec_idx)
956{
957 current_codec = codec_idx;
958 dsp_configure(DSP_SWITCH_CODEC, codec_idx);
959}
960
961#ifdef PLAYBACK_VOICE
962static void swap_codec(void)
963{
964 int my_codec;
965
966 /* Swap nothing if no swap buffers exist */
967 if (dram_buf == NULL)
968 {
969 logf("swap: no swap buffers");
970 return;
971 }
972
973 my_codec = current_codec;
974
975 logf("swapping out codec: %d", my_codec);
976
977 /* Invert this when a codec thread enters and leaves */
978 swap_codec_parity = !swap_codec_parity;
979
980 /* If this is true, an odd number of calls has occurred and there's
981 no codec thread waiting to swap us out when it locks and runs. This
982 occurs when playback is stopped or when just starting playback and
983 the audio thread is loading a codec; parities should always be even
984 on entry when a thread calls this during playback */
985 if (swap_codec_parity)
986 {
987 /* Save our current IRAM and DRAM */
988#ifdef IRAM_STEAL
989 if (voice_iram_stolen)
990 {
991 logf("swap: iram restore");
992 voice_iram_stolen = false;
993 /* Don't swap trashed data into buffer as the voice IRAM will
994 already be swapped out - should _always_ be the case if
995 voice_iram_stolen is true since the voice has been swapped
996 in beforehand */
997 if (my_codec == CODEC_IDX_VOICE)
998 {
999 logf("voice iram already swapped");
1000 goto skip_iram_swap;
1001 }
1002 }
1003#endif
1004
1005 memswap128(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE);
1006
1007#ifdef IRAM_STEAL
1008 skip_iram_swap:
1009#endif
1010
1011 memswap128(dram_buf, codecbuf, CODEC_SIZE);
1012 /* No cache invalidation needed; it will be done in codec_load_ram
1013 or we won't be here otherwise */
1014 }
1015
1016 /* Release my semaphore */
1017 semaphore_release(&sem_codecthread);
1018 logf("unlocked: %d", my_codec);
1019
1020 /* Wait for other codec */
1021 event_wait(&event_codecthread,
1022 (my_codec == CODEC_IDX_AUDIO) ? STATE_NONSIGNALED : STATE_SIGNALED);
1023
1024 /* Wait for other codec to unlock */
1025 logf("waiting for lock: %d", my_codec);
1026 semaphore_wait(&sem_codecthread);
1027
1028 /* Take control */
1029 set_current_codec(my_codec);
1030 event_set_state(&event_codecthread,
1031 (my_codec == CODEC_IDX_AUDIO) ? STATE_SIGNALED : STATE_NONSIGNALED);
1032
1033 /* Reload our IRAM and DRAM */
1034 memswap128(iram_buf, CODEC_IRAM_ORIGIN, CODEC_IRAM_SIZE);
1035 memswap128(dram_buf, codecbuf, CODEC_SIZE);
1036 invalidate_icache();
1037
1038 /* Flip parity again */
1039 swap_codec_parity = !swap_codec_parity;
1040
1041 logf("resuming codec: %d", my_codec);
1042}
1043
1044/* This function is meant to be used by the buffer stealing functions to
1045 ensure the codec is no longer active and so voice will be swapped-in
1046 before it is called */
1047static void voice_stop(void)
1048{
1049 /* Must have a voice codec loaded or we'll hang forever here */
1050 if (!voice_codec_loaded)
1051 return;
1052
1053 talk_force_shutup();
1054
1055 /* Loop until voice empties it's queue, stops and picks up on the new
1056 track; the voice thread must be stopped and waiting for messages
1057 outside the codec */
1058 while (voice_is_playing || !queue_empty(&voice_queue) ||
1059 ci_voice.new_track)
1060 yield();
1061
1062 if (!playing)
1063 pcmbuf_play_stop();
1064} /* voice_stop */
1065
1066/* Is voice still speaking */
1067/* Unfortunately only reliable when music is not also playing. */
1068static bool is_voice_speaking(void)
1069{
1070 return is_voice_queued()
1071 || voice_is_playing
1072 || (!playing && pcm_is_playing());
1073}
1074
1075#endif /* PLAYBACK_VOICE */
1076
1077/* Wait for voice to finish speaking. */
1078/* Also only reliable when music is not also playing. */
1079void voice_wait(void)
1080{
1081#ifdef PLAYBACK_VOICE
1082 while (is_voice_speaking())
1083 sleep(HZ/10);
1084#endif
1085}
1086 790
1087static void set_filebuf_watermark(int seconds, size_t max) 791static void set_filebuf_watermark(int seconds, size_t max)
1088{ 792{
@@ -1126,303 +830,6 @@ const char * get_codec_filename(int cod_spec)
1126 return fname; 830 return fname;
1127} /* get_codec_filename */ 831} /* get_codec_filename */
1128 832
1129
1130/* --- Voice thread --- */
1131
1132#ifdef PLAYBACK_VOICE
1133
1134static bool voice_pcmbuf_insert_callback(
1135 const void *ch1, const void *ch2, int count)
1136{
1137 const char *src[2] = { ch1, ch2 };
1138
1139 while (count > 0)
1140 {
1141 int out_count = dsp_output_count(count);
1142 int inp_count;
1143 char *dest;
1144
1145 while ((dest = pcmbuf_request_voice_buffer(
1146 &out_count, playing)) == NULL)
1147 {
1148 if (playing && audio_codec_loaded)
1149 swap_codec();
1150 else
1151 yield();
1152 }
1153
1154 /* Get the real input_size for output_size bytes, guarding
1155 * against resampling buffer overflows. */
1156 inp_count = dsp_input_count(out_count);
1157
1158 if (inp_count <= 0)
1159 return true;
1160
1161 /* Input size has grown, no error, just don't write more than length */
1162 if (inp_count > count)
1163 inp_count = count;
1164
1165 out_count = dsp_process(dest, src, inp_count);
1166
1167 if (out_count <= 0)
1168 return true;
1169
1170 if (playing)
1171 {
1172 pcmbuf_mix_voice(out_count);
1173 if ((pcmbuf_usage() < 10 || pcmbuf_mix_free() < 30) &&
1174 audio_codec_loaded)
1175 swap_codec();
1176 }
1177 else
1178 pcmbuf_write_complete(out_count);
1179
1180 count -= inp_count;
1181 }
1182
1183 return true;
1184} /* voice_pcmbuf_insert_callback */
1185
1186static void* voice_get_memory_callback(size_t *size)
1187{
1188 /* Voice should have no use for this. If it did, we'd have to
1189 swap the malloc buffer as well. */
1190 *size = 0;
1191 return NULL;
1192}
1193
1194static void voice_set_elapsed_callback(unsigned int value)
1195{
1196 (void)value;
1197}
1198
1199static void voice_set_offset_callback(size_t value)
1200{
1201 (void)value;
1202}
1203
1204static void voice_configure_callback(int setting, intptr_t value)
1205{
1206 if (!dsp_configure(setting, value))
1207 {
1208 logf("Illegal key:%d", setting);
1209 }
1210}
1211
1212static size_t voice_filebuf_callback(void *ptr, size_t size)
1213{
1214 (void)ptr;
1215 (void)size;
1216
1217 return 0;
1218}
1219
1220/* Handle Q_VOICE_STOP and part of SYS_USB_CONNECTED */
1221static bool voice_on_voice_stop(bool aborting, size_t *realsize)
1222{
1223 if (aborting && !playing)
1224 {
1225 /* Aborting: Slight hack - flush PCM buffer if
1226 only being used for voice */
1227 pcmbuf_play_stop();
1228 }
1229
1230 if (voice_is_playing)
1231 {
1232 /* Clear the current buffer */
1233 voice_is_playing = false;
1234 voice_getmore = NULL;
1235 voice_remaining = 0;
1236 voicebuf = NULL;
1237
1238 /* Cancel any automatic boost if no more clips requested. */
1239 if (!playing || !voice_thread_start)
1240 sleep(0);
1241
1242 /* Force the codec to think it's changing tracks */
1243 ci_voice.new_track = 1;
1244
1245 *realsize = 0;
1246 return true; /* Yes, change tracks */
1247 }
1248
1249 return false;
1250}
1251
1252static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
1253{
1254 struct queue_event ev;
1255
1256 if (ci_voice.new_track)
1257 {
1258 *realsize = 0;
1259 return NULL;
1260 }
1261
1262 while (1)
1263 {
1264 if (voice_is_playing || playing)
1265 {
1266 queue_wait_w_tmo(&voice_queue, &ev, 0);
1267 if (!voice_is_playing && ev.id == SYS_TIMEOUT)
1268 ev.id = Q_AUDIO_PLAY;
1269 }
1270 else
1271 {
1272 queue_wait(&voice_queue, &ev);
1273 }
1274
1275 switch (ev.id) {
1276 case Q_AUDIO_PLAY:
1277 LOGFQUEUE("voice < Q_AUDIO_PLAY");
1278 if (playing)
1279 {
1280 if (audio_codec_loaded)
1281 swap_codec();
1282 yield();
1283 }
1284 break;
1285
1286#ifdef AUDIO_HAVE_RECORDING
1287 case Q_ENCODER_RECORD:
1288 LOGFQUEUE("voice < Q_ENCODER_RECORD");
1289 swap_codec();
1290 break;
1291#endif
1292
1293 case Q_VOICE_STOP:
1294 LOGFQUEUE("voice < Q_VOICE_STOP");
1295 if (voice_on_voice_stop(ev.data, realsize))
1296 return NULL;
1297 break;
1298
1299 case Q_VOICE_PLAY:
1300 LOGFQUEUE("voice < Q_VOICE_PLAY");
1301 if (!voice_is_playing)
1302 {
1303 /* Set up new voice data */
1304 struct voice_info *voice_data;
1305#ifdef IRAM_STEAL
1306 if (voice_iram_stolen)
1307 {
1308 /* Voice is the first to run again and is currently
1309 loaded */
1310 logf("voice: iram restore");
1311 memcpy(CODEC_IRAM_ORIGIN, iram_buf, CODEC_IRAM_SIZE);
1312 voice_iram_stolen = false;
1313 }
1314#endif
1315 /* Must reset the buffer before any playback begins if
1316 needed */
1317 if (buffer_state == BUFFER_STATE_TRASHED)
1318 audio_reset_buffer();
1319
1320 voice_is_playing = true;
1321 trigger_cpu_boost();
1322 voice_data = (struct voice_info *)ev.data;
1323 voice_remaining = voice_data->size;
1324 voicebuf = voice_data->buf;
1325 voice_getmore = voice_data->callback;
1326 }
1327 goto voice_play_clip; /* To exit both switch and while */
1328
1329 case SYS_TIMEOUT:
1330 LOGFQUEUE_SYS_TIMEOUT("voice < SYS_TIMEOUT");
1331 goto voice_play_clip;
1332
1333 default:
1334 LOGFQUEUE("voice < default");
1335 }
1336 }
1337
1338voice_play_clip:
1339
1340 if (voice_remaining == 0 || voicebuf == NULL)
1341 {
1342 if (voice_getmore)
1343 voice_getmore((unsigned char **)&voicebuf, &voice_remaining);
1344
1345 /* If this clip is done */
1346 if (voice_remaining == 0)
1347 {
1348 LOGFQUEUE("voice > voice Q_VOICE_STOP");
1349 queue_post(&voice_queue, Q_VOICE_STOP, 0);
1350 /* Force pcm playback. */
1351 if (!pcm_is_playing())
1352 pcmbuf_play_start();
1353 }
1354 }
1355
1356 *realsize = MIN(voice_remaining, reqsize);
1357
1358 if (*realsize == 0)
1359 return NULL;
1360
1361 return voicebuf;
1362} /* voice_request_buffer_callback */
1363
1364static void voice_advance_buffer_callback(size_t amount)
1365{
1366 amount = MIN(amount, voice_remaining);
1367 voicebuf += amount;
1368 voice_remaining -= amount;
1369}
1370
1371static void voice_advance_buffer_loc_callback(void *ptr)
1372{
1373 size_t amount = (size_t)ptr - (size_t)voicebuf;
1374
1375 voice_advance_buffer_callback(amount);
1376}
1377
1378static off_t voice_mp3_get_filepos_callback(int newtime)
1379{
1380 (void)newtime;
1381
1382 return 0;
1383}
1384
1385static void voice_do_nothing(void)
1386{
1387 return;
1388}
1389
1390static bool voice_seek_buffer_callback(size_t newpos)
1391{
1392 (void)newpos;
1393
1394 return false;
1395}
1396
1397static bool voice_request_next_track_callback(void)
1398{
1399 ci_voice.new_track = 0;
1400 return true;
1401}
1402
1403static void voice_thread(void)
1404{
1405 logf("Loading voice codec");
1406 voice_codec_loaded = true;
1407 semaphore_wait(&sem_codecthread);
1408 event_set_state(&event_codecthread, STATE_NONSIGNALED);
1409 set_current_codec(CODEC_IDX_VOICE);
1410 dsp_configure(DSP_RESET, 0);
1411 voice_remaining = 0;
1412 voice_getmore = NULL;
1413
1414 /* FIXME: If we being starting the voice thread without reboot, the
1415 voice_queue could be full of old stuff and we must flush it. */
1416 codec_load_file(get_codec_filename(AFMT_MPA_L3), &ci_voice);
1417
1418 logf("Voice codec finished");
1419 voice_codec_loaded = false;
1420 voice_thread_p = NULL;
1421 semaphore_release(&sem_codecthread);
1422} /* voice_thread */
1423
1424#endif /* PLAYBACK_VOICE */
1425
1426/* --- Codec thread --- */ 833/* --- Codec thread --- */
1427static bool codec_pcmbuf_insert_callback( 834static bool codec_pcmbuf_insert_callback(
1428 const void *ch1, const void *ch2, int count) 835 const void *ch1, const void *ch2, int count)
@@ -1431,7 +838,7 @@ static bool codec_pcmbuf_insert_callback(
1431 838
1432 while (count > 0) 839 while (count > 0)
1433 { 840 {
1434 int out_count = dsp_output_count(count); 841 int out_count = dsp_output_count(ci.dsp, count);
1435 int inp_count; 842 int inp_count;
1436 char *dest; 843 char *dest;
1437 844
@@ -1448,7 +855,7 @@ static bool codec_pcmbuf_insert_callback(
1448 855
1449 /* Get the real input_size for output_size bytes, guarding 856 /* Get the real input_size for output_size bytes, guarding
1450 * against resampling buffer overflows. */ 857 * against resampling buffer overflows. */
1451 inp_count = dsp_input_count(out_count); 858 inp_count = dsp_input_count(ci.dsp, out_count);
1452 859
1453 if (inp_count <= 0) 860 if (inp_count <= 0)
1454 return true; 861 return true;
@@ -1457,23 +864,13 @@ static bool codec_pcmbuf_insert_callback(
1457 if (inp_count > count) 864 if (inp_count > count)
1458 inp_count = count; 865 inp_count = count;
1459 866
1460 out_count = dsp_process(dest, src, inp_count); 867 out_count = dsp_process(ci.dsp, dest, src, inp_count);
1461 868
1462 if (out_count <= 0) 869 if (out_count <= 0)
1463 return true; 870 return true;
1464 871
1465 pcmbuf_write_complete(out_count); 872 pcmbuf_write_complete(out_count);
1466 873
1467#ifdef PLAYBACK_VOICE
1468 if ((voice_is_playing || voice_thread_start)
1469 && pcm_is_playing() && voice_codec_loaded &&
1470 pcmbuf_usage() > 30 && pcmbuf_mix_free() > 80)
1471 {
1472 voice_thread_start = false;
1473 swap_codec();
1474 }
1475#endif
1476
1477 count -= inp_count; 874 count -= inp_count;
1478 } 875 }
1479 876
@@ -1690,7 +1087,7 @@ static void codec_seek_complete_callback(void)
1690 { 1087 {
1691 /* If this is not a seamless seek, clear the buffer */ 1088 /* If this is not a seamless seek, clear the buffer */
1692 pcmbuf_play_stop(); 1089 pcmbuf_play_stop();
1693 dsp_configure(DSP_FLUSH, 0); 1090 dsp_configure(ci.dsp, DSP_FLUSH, 0);
1694 1091
1695 /* If playback was not 'deliberately' paused, unpause now */ 1092 /* If playback was not 'deliberately' paused, unpause now */
1696 if (!paused) 1093 if (!paused)
@@ -1721,7 +1118,8 @@ static void codec_configure_callback(int setting, intptr_t value)
1721 break; 1118 break;
1722 1119
1723 default: 1120 default:
1724 if (!dsp_configure(setting, value)) { logf("Illegal key:%d", setting); } 1121 if (!dsp_configure(ci.dsp, setting, value))
1122 { logf("Illegal key:%d", setting); }
1725 } 1123 }
1726} 1124}
1727 1125
@@ -1886,23 +1284,8 @@ static void codec_thread(void)
1886 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); 1284 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
1887 queue_reply(&codec_queue, 1); 1285 queue_reply(&codec_queue, 1);
1888 audio_codec_loaded = true; 1286 audio_codec_loaded = true;
1889#ifdef PLAYBACK_VOICE
1890 /* Don't sent messages to voice codec if it's already swapped
1891 out or it will never get this */
1892 if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE)
1893 {
1894 LOGFQUEUE("codec > voice Q_AUDIO_PLAY");
1895 queue_post(&voice_queue, Q_AUDIO_PLAY, 0);
1896 }
1897 semaphore_wait(&sem_codecthread);
1898 event_set_state(&event_codecthread, STATE_SIGNALED);
1899#endif
1900 set_current_codec(CODEC_IDX_AUDIO);
1901 ci.stop_codec = false; 1287 ci.stop_codec = false;
1902 status = codec_load_file((const char *)ev.data, &ci); 1288 status = codec_load_file((const char *)ev.data, &ci);
1903#ifdef PLAYBACK_VOICE
1904 semaphore_release(&sem_codecthread);
1905#endif
1906 break; 1289 break;
1907 1290
1908 case Q_CODEC_LOAD: 1291 case Q_CODEC_LOAD:
@@ -1920,43 +1303,17 @@ static void codec_thread(void)
1920 } 1303 }
1921 1304
1922 audio_codec_loaded = true; 1305 audio_codec_loaded = true;
1923#ifdef PLAYBACK_VOICE
1924 if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE)
1925 {
1926 LOGFQUEUE("codec > voice Q_AUDIO_PLAY");
1927 queue_post(&voice_queue, Q_AUDIO_PLAY, 0);
1928 }
1929 semaphore_wait(&sem_codecthread);
1930 event_set_state(&event_codecthread, STATE_SIGNALED);
1931#endif
1932 set_current_codec(CODEC_IDX_AUDIO);
1933 ci.stop_codec = false; 1306 ci.stop_codec = false;
1934 status = codec_load_buf(CUR_TI->codec_hid, &ci); 1307 status = codec_load_buf(CUR_TI->codec_hid, &ci);
1935#ifdef PLAYBACK_VOICE
1936 semaphore_release(&sem_codecthread);
1937#endif
1938 break; 1308 break;
1939 1309
1940#ifdef AUDIO_HAVE_RECORDING 1310#ifdef AUDIO_HAVE_RECORDING
1941 case Q_ENCODER_LOAD_DISK: 1311 case Q_ENCODER_LOAD_DISK:
1942 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK"); 1312 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
1943 audio_codec_loaded = false; /* Not audio codec! */ 1313 audio_codec_loaded = false; /* Not audio codec! */
1944#ifdef PLAYBACK_VOICE
1945 if (voice_codec_loaded && current_codec == CODEC_IDX_VOICE)
1946 {
1947 LOGFQUEUE("codec > voice Q_ENCODER_RECORD");
1948 queue_post(&voice_queue, Q_ENCODER_RECORD, 0);
1949 }
1950 semaphore_wait(&sem_codecthread);
1951 event_set_state(&event_codecthread, STATE_SIGNALED);
1952#endif
1953 logf("loading encoder"); 1314 logf("loading encoder");
1954 set_current_codec(CODEC_IDX_AUDIO);
1955 ci.stop_encoder = false; 1315 ci.stop_encoder = false;
1956 status = codec_load_file((const char *)ev.data, &ci); 1316 status = codec_load_file((const char *)ev.data, &ci);
1957#ifdef PLAYBACK_VOICE
1958 semaphore_release(&sem_codecthread);
1959#endif
1960 logf("encoder stopped"); 1317 logf("encoder stopped");
1961 break; 1318 break;
1962#endif /* AUDIO_HAVE_RECORDING */ 1319#endif /* AUDIO_HAVE_RECORDING */
@@ -2341,13 +1698,8 @@ static bool audio_load_track(int offset, bool start_play)
2341 /* Set default values */ 1698 /* Set default values */
2342 if (start_play) 1699 if (start_play)
2343 { 1700 {
2344 int last_codec = current_codec;
2345
2346 set_current_codec(CODEC_IDX_AUDIO);
2347 buf_set_watermark(AUDIO_DEFAULT_WATERMARK); 1701 buf_set_watermark(AUDIO_DEFAULT_WATERMARK);
2348 dsp_configure(DSP_RESET, 0); 1702 dsp_configure(ci.dsp, DSP_RESET, 0);
2349 set_current_codec(last_codec);
2350
2351 track_changed = true; 1703 track_changed = true;
2352 playlist_update_resume_info(audio_current_track()); 1704 playlist_update_resume_info(audio_current_track());
2353 } 1705 }
@@ -3001,67 +2353,7 @@ static void audio_reset_buffer(void)
3001 filebuf = malloc_buf + MALLOC_BUFSIZE; /* filebuf line align implied */ 2353 filebuf = malloc_buf + MALLOC_BUFSIZE; /* filebuf line align implied */
3002 filebuflen = audiobufend - filebuf; 2354 filebuflen = audiobufend - filebuf;
3003 2355
3004 /* Allow for codec swap space at end of audio buffer */ 2356 filebuflen &= ~15;
3005 if (talk_voice_required())
3006 {
3007 /* Layout of swap buffer:
3008 * #ifdef IRAM_STEAL (dedicated iram_buf):
3009 * |iram_buf|...audiobuf...|dram_buf|audiobufend
3010 * #else:
3011 * audiobuf...|dram_buf|iram_buf|audiobufend
3012 */
3013#ifdef PLAYBACK_VOICE
3014 /* Check for an absolutely nasty situation which should never,
3015 ever happen - frankly should just panic */
3016 if (voice_codec_loaded && current_codec != CODEC_IDX_VOICE)
3017 {
3018 logf("buffer reset with voice swapped");
3019 }
3020 /* line align length which line aligns the calculations below since
3021 all sizes are also at least line aligned - needed for memswap128 */
3022 filebuflen &= ~15;
3023#ifdef IRAM_STEAL
3024 filebuflen -= CODEC_SIZE;
3025#else
3026 filebuflen -= CODEC_SIZE + CODEC_IRAM_SIZE;
3027#endif
3028 /* Allocate buffers for swapping voice <=> audio */
3029 /* If using IRAM for plugins voice IRAM swap buffer must be dedicated
3030 and out of the way of buffer usage or else a call to audio_get_buffer
3031 and subsequent buffer use might trash the swap space. A plugin
3032 initializing IRAM after getting the full buffer would present similar
3033 problem. Options include: failing the request if the other buffer
3034 has been obtained already or never allowing use of the voice IRAM
3035 buffer within the audio buffer. Using buffer_alloc basically
3036 implements the second in a more convenient way. */
3037 dram_buf = filebuf + filebuflen;
3038
3039#ifdef IRAM_STEAL
3040 /* Allocate voice IRAM swap buffer once */
3041 if (iram_buf == NULL)
3042 {
3043 iram_buf = buffer_alloc(CODEC_IRAM_SIZE);
3044 /* buffer_alloc moves audiobuf; this is safe because only the end
3045 * has been touched so far in this function and the address of
3046 * filebuf + filebuflen is not changed */
3047 malloc_buf += CODEC_IRAM_SIZE;
3048 filebuf += CODEC_IRAM_SIZE;
3049 filebuflen -= CODEC_IRAM_SIZE;
3050 }
3051#else
3052 /* Allocate iram_buf after dram_buf */
3053 iram_buf = dram_buf + CODEC_SIZE;
3054#endif /* IRAM_STEAL */
3055#endif /* PLAYBACK_VOICE */
3056 }
3057 else
3058 {
3059#ifdef PLAYBACK_VOICE
3060 /* No swap buffers needed */
3061 iram_buf = NULL;
3062 dram_buf = NULL;
3063#endif
3064 }
3065 2357
3066 /* Subtract whatever the pcm buffer says it used plus the guard buffer */ 2358 /* Subtract whatever the pcm buffer says it used plus the guard buffer */
3067 filebuflen -= pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE; 2359 filebuflen -= pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE;
@@ -3090,16 +2382,6 @@ static void audio_reset_buffer(void)
3090 logf("gbufe: %08X", (unsigned)(filebuf + filebuflen + GUARD_BUFSIZE)); 2382 logf("gbufe: %08X", (unsigned)(filebuf + filebuflen + GUARD_BUFSIZE));
3091 logf("pcmb: %08X", (unsigned)pcmbuf); 2383 logf("pcmb: %08X", (unsigned)pcmbuf);
3092 logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize)); 2384 logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize));
3093 if (dram_buf)
3094 {
3095 logf("dramb: %08X", (unsigned)dram_buf);
3096 logf("drambe: %08X", (unsigned)(dram_buf + CODEC_SIZE));
3097 }
3098 if (iram_buf)
3099 {
3100 logf("iramb: %08X", (unsigned)iram_buf);
3101 logf("irambe: %08X", (unsigned)(iram_buf + CODEC_IRAM_SIZE));
3102 }
3103 } 2385 }
3104#endif 2386#endif
3105} 2387}
@@ -3110,21 +2392,6 @@ static void audio_thread(void)
3110 2392
3111 pcm_postinit(); 2393 pcm_postinit();
3112 2394
3113#ifdef PLAYBACK_VOICE
3114 /* Unlock semaphore that init stage locks before creating this thread */
3115 semaphore_release(&sem_codecthread);
3116
3117 /* Buffers must be set up by now - should panic - really */
3118 if (buffer_state != BUFFER_STATE_INITIALIZED)
3119 {
3120 logf("audio_thread start: no buffer");
3121 }
3122
3123 /* Have to wait for voice to load up or else the codec swap will be
3124 invalid when an audio codec is loaded */
3125 wait_for_voice_swap_in();
3126#endif
3127
3128 while (1) 2395 while (1)
3129 { 2396 {
3130 queue_wait_w_tmo(&audio_queue, &ev, HZ/2); 2397 queue_wait_w_tmo(&audio_queue, &ev, HZ/2);
@@ -3214,7 +2481,6 @@ static void audio_thread(void)
3214 if (playing) 2481 if (playing)
3215 audio_stop_playback(); 2482 audio_stop_playback();
3216#ifdef PLAYBACK_VOICE 2483#ifdef PLAYBACK_VOICE
3217 wait_for_voice_swap_in();
3218 voice_stop(); 2484 voice_stop();
3219#endif 2485#endif
3220 usb_acknowledge(SYS_USB_CONNECTED_ACK); 2486 usb_acknowledge(SYS_USB_CONNECTED_ACK);
@@ -3253,11 +2519,6 @@ static void audio_test_track_changed_event(struct mp3entry *id3)
3253 */ 2519 */
3254void audio_init(void) 2520void audio_init(void)
3255{ 2521{
3256#ifdef PLAYBACK_VOICE
3257 static bool voicetagtrue = true;
3258 static struct mp3entry id3_voice;
3259 struct thread_entry *voice_thread_p = NULL;
3260#endif
3261 struct thread_entry *audio_thread_p; 2522 struct thread_entry *audio_thread_p;
3262 2523
3263 /* Can never do this twice */ 2524 /* Can never do this twice */
@@ -3272,13 +2533,6 @@ void audio_init(void)
3272 /* Initialize queues before giving control elsewhere in case it likes 2533 /* Initialize queues before giving control elsewhere in case it likes
3273 to send messages. Thread creation will be delayed however so nothing 2534 to send messages. Thread creation will be delayed however so nothing
3274 starts running until ready if something yields such as talk_init. */ 2535 starts running until ready if something yields such as talk_init. */
3275#ifdef PLAYBACK_VOICE
3276 /* Take ownership of lock to prevent playback of anything before audio
3277 hardware is initialized - audio thread unlocks it after final init
3278 stage */
3279 semaphore_init(&sem_codecthread, 1, 0);
3280 event_init(&event_codecthread, EVENT_MANUAL | STATE_SIGNALED);
3281#endif
3282 queue_init(&audio_queue, true); 2536 queue_init(&audio_queue, true);
3283 queue_enable_queue_send(&audio_queue, &audio_queue_sender_list); 2537 queue_enable_queue_send(&audio_queue, &audio_queue_sender_list);
3284 queue_init(&codec_queue, false); 2538 queue_init(&codec_queue, false);
@@ -3305,30 +2559,8 @@ void audio_init(void)
3305 ci.set_offset = codec_set_offset_callback; 2559 ci.set_offset = codec_set_offset_callback;
3306 ci.configure = codec_configure_callback; 2560 ci.configure = codec_configure_callback;
3307 ci.discard_codec = codec_discard_codec_callback; 2561 ci.discard_codec = codec_discard_codec_callback;
3308 2562 ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
3309 /* Initialize voice codec api. */ 2563 CODEC_IDX_AUDIO);
3310#ifdef PLAYBACK_VOICE
3311 memcpy(&ci_voice, &ci, sizeof(ci_voice));
3312 memset(&id3_voice, 0, sizeof(id3_voice));
3313 ci_voice.read_filebuf = voice_filebuf_callback;
3314 ci_voice.pcmbuf_insert = voice_pcmbuf_insert_callback;
3315 ci_voice.get_codec_memory = voice_get_memory_callback;
3316 ci_voice.request_buffer = voice_request_buffer_callback;
3317 ci_voice.advance_buffer = voice_advance_buffer_callback;
3318 ci_voice.advance_buffer_loc = voice_advance_buffer_loc_callback;
3319 ci_voice.request_next_track = voice_request_next_track_callback;
3320 ci_voice.mp3_get_filepos = voice_mp3_get_filepos_callback;
3321 ci_voice.seek_buffer = voice_seek_buffer_callback;
3322 ci_voice.seek_complete = voice_do_nothing;
3323 ci_voice.set_elapsed = voice_set_elapsed_callback;
3324 ci_voice.set_offset = voice_set_offset_callback;
3325 ci_voice.configure = voice_configure_callback;
3326 ci_voice.discard_codec = voice_do_nothing;
3327 ci_voice.taginfo_ready = &voicetagtrue;
3328 ci_voice.id3 = &id3_voice;
3329 id3_voice.frequency = 11200;
3330 id3_voice.length = 1000000L;
3331#endif
3332 2564
3333 /* initialize the buffer */ 2565 /* initialize the buffer */
3334 filebuf = audiobuf; 2566 filebuf = audiobuf;
@@ -3349,16 +2581,7 @@ void audio_init(void)
3349 IF_COP(, CPU)); 2581 IF_COP(, CPU));
3350 2582
3351#ifdef PLAYBACK_VOICE 2583#ifdef PLAYBACK_VOICE
3352 /* TODO: Change this around when various speech codecs can be used */ 2584 voice_thread_init();
3353 if (talk_voice_required())
3354 {
3355 logf("Starting voice codec");
3356 queue_init(&voice_queue, false);
3357 voice_thread_p = create_thread(voice_thread, voice_stack,
3358 sizeof(voice_stack), CREATE_THREAD_FROZEN,
3359 voice_thread_name
3360 IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU));
3361 }
3362#endif 2585#endif
3363 2586
3364 /* Set crossfade setting for next buffer init which should be about... */ 2587 /* Set crossfade setting for next buffer init which should be about... */
@@ -3393,11 +2616,10 @@ void audio_init(void)
3393#endif 2616#endif
3394 2617
3395 /* it's safe to let the threads run now */ 2618 /* it's safe to let the threads run now */
3396 thread_thaw(codec_thread_p);
3397#ifdef PLAYBACK_VOICE 2619#ifdef PLAYBACK_VOICE
3398 if (voice_thread_p) 2620 voice_thread_resume();
3399 thread_thaw(voice_thread_p);
3400#endif 2621#endif
2622 thread_thaw(codec_thread_p);
3401 thread_thaw(audio_thread_p); 2623 thread_thaw(audio_thread_p);
3402} /* audio_init */
3403 2624
2625} /* audio_init */
diff --git a/apps/plugin.c b/apps/plugin.c
index a80e9dd86d..145d30b41b 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -457,7 +457,7 @@ static const struct plugin_api rockbox_api = {
457 plugin_get_audio_buffer, 457 plugin_get_audio_buffer,
458 plugin_tsr, 458 plugin_tsr,
459 plugin_get_current_filename, 459 plugin_get_current_filename,
460#ifdef IRAM_STEAL 460#ifdef PLUGIN_USE_IRAM
461 plugin_iram_init, 461 plugin_iram_init,
462#endif 462#endif
463#if defined(DEBUG) || defined(SIMULATOR) 463#if defined(DEBUG) || defined(SIMULATOR)
@@ -732,12 +732,13 @@ void* plugin_get_audio_buffer(size_t *buffer_size)
732#endif 732#endif
733} 733}
734 734
735#ifdef IRAM_STEAL 735#ifdef PLUGIN_USE_IRAM
736/* Initializes plugin IRAM */ 736/* Initializes plugin IRAM */
737void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size, 737void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size,
738 char *iedata, size_t iedata_size) 738 char *iedata, size_t iedata_size)
739{ 739{
740 audio_iram_steal(); 740 /* We need to stop audio playback in order to use codec IRAM */
741 audio_hard_stop();
741 memcpy(iramstart, iramcopy, iram_size); 742 memcpy(iramstart, iramcopy, iram_size);
742 memset(iedata, 0, iedata_size); 743 memset(iedata, 0, iedata_size);
743 memset(iramcopy, 0, iram_size); 744 memset(iramcopy, 0, iram_size);
@@ -746,7 +747,7 @@ void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size,
746 flush_icache(); 747 flush_icache();
747#endif 748#endif
748} 749}
749#endif /* IRAM_STEAL */ 750#endif /* PLUGIN_USE_IRAM */
750 751
751/* The plugin wants to stay resident after leaving its main function, e.g. 752/* The plugin wants to stay resident after leaving its main function, e.g.
752 runs from timer or own thread. The callback is registered to later 753 runs from timer or own thread. The callback is registered to later
diff --git a/apps/plugin.h b/apps/plugin.h
index 75d8654240..9123af44ec 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -568,7 +568,7 @@ struct plugin_api {
568 void* (*plugin_get_audio_buffer)(size_t *buffer_size); 568 void* (*plugin_get_audio_buffer)(size_t *buffer_size);
569 void (*plugin_tsr)(bool (*exit_callback)(bool reenter)); 569 void (*plugin_tsr)(bool (*exit_callback)(bool reenter));
570 char* (*plugin_get_current_filename)(void); 570 char* (*plugin_get_current_filename)(void);
571#ifdef IRAM_STEAL 571#ifdef PLUGIN_USE_IRAM
572 void (*plugin_iram_init)(char *iramstart, char *iramcopy, size_t iram_size, 572 void (*plugin_iram_init)(char *iramstart, char *iramcopy, size_t iram_size,
573 char *iedata, size_t iedata_size); 573 char *iedata, size_t iedata_size);
574#endif 574#endif
@@ -674,7 +674,7 @@ extern unsigned char plugin_end_addr[];
674 NULL, NULL, plugin_start }; 674 NULL, NULL, plugin_start };
675#endif /* SIMULATOR */ 675#endif /* SIMULATOR */
676 676
677#ifdef USE_IRAM 677#ifdef PLUGIN_USE_IRAM
678/* Declare IRAM variables */ 678/* Declare IRAM variables */
679#define PLUGIN_IRAM_DECLARE \ 679#define PLUGIN_IRAM_DECLARE \
680 extern char iramcopy[]; \ 680 extern char iramcopy[]; \
@@ -689,13 +689,13 @@ extern unsigned char plugin_end_addr[];
689#else 689#else
690#define PLUGIN_IRAM_DECLARE 690#define PLUGIN_IRAM_DECLARE
691#define PLUGIN_IRAM_INIT(api) 691#define PLUGIN_IRAM_INIT(api)
692#endif /* USE_IRAM */ 692#endif /* PLUGIN_USE_IRAM */
693#endif /* PLUGIN */ 693#endif /* PLUGIN */
694 694
695int plugin_load(const char* plugin, void* parameter); 695int plugin_load(const char* plugin, void* parameter);
696void* plugin_get_buffer(size_t *buffer_size); 696void* plugin_get_buffer(size_t *buffer_size);
697void* plugin_get_audio_buffer(size_t *buffer_size); 697void* plugin_get_audio_buffer(size_t *buffer_size);
698#ifdef IRAM_STEAL 698#ifdef PLUGIN_USE_IRAM
699void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size, 699void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size,
700 char *iedata, size_t iedata_size); 700 char *iedata, size_t iedata_size);
701#endif 701#endif
diff --git a/apps/talk.c b/apps/talk.c
index 53729ccd7d..332a10f820 100644
--- a/apps/talk.c
+++ b/apps/talk.c
@@ -56,8 +56,6 @@
56 | | filebuf 56 | | filebuf
57 | |------------ 57 | |------------
58 | | audio 58 | | audio
59 | |------------
60 | | codec swap
61 audiobufend----------+-----------+------------ 59 audiobufend----------+-----------+------------
62 60
63 SWCODEC allocates dedicated buffers, MASCODEC reuses audiobuf. */ 61 SWCODEC allocates dedicated buffers, MASCODEC reuses audiobuf. */
@@ -628,7 +626,9 @@ int talk_file(const char* filename, bool enqueue)
628{ 626{
629 int fd; 627 int fd;
630 int size; 628 int size;
629#if CONFIG_CODEC != SWCODEC
631 struct mp3entry info; 630 struct mp3entry info;
631#endif
632 632
633 if (talk_temp_disable_count > 0) 633 if (talk_temp_disable_count > 0)
634 return -1; /* talking has been disabled */ 634 return -1; /* talking has been disabled */
@@ -640,10 +640,12 @@ int talk_file(const char* filename, bool enqueue)
640 if (p_thumbnail == NULL || size_for_thumbnail <= 0) 640 if (p_thumbnail == NULL || size_for_thumbnail <= 0)
641 return -1; 641 return -1;
642 642
643#if CONFIG_CODEC != SWCODEC
643 if(mp3info(&info, filename)) /* use this to find real start */ 644 if(mp3info(&info, filename)) /* use this to find real start */
644 { 645 {
645 return 0; /* failed to open, or invalid */ 646 return 0; /* failed to open, or invalid */
646 } 647 }
648#endif
647 649
648 fd = open(filename, O_RDONLY); 650 fd = open(filename, O_RDONLY);
649 if (fd < 0) /* failed to open */ 651 if (fd < 0) /* failed to open */
@@ -651,14 +653,16 @@ int talk_file(const char* filename, bool enqueue)
651 return 0; 653 return 0;
652 } 654 }
653 655
656#if CONFIG_CODEC != SWCODEC
654 lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */ 657 lseek(fd, info.first_frame_offset, SEEK_SET); /* behind ID data */
658#endif
655 659
656 size = read(fd, p_thumbnail, size_for_thumbnail); 660 size = read(fd, p_thumbnail, size_for_thumbnail);
657 close(fd); 661 close(fd);
658 662
659 /* ToDo: find audio, skip ID headers and trailers */ 663 /* ToDo: find audio, skip ID headers and trailers */
660 664
661 if (size != 0 && size != size_for_thumbnail) /* Don't play missing or truncated clips */ 665 if (size > 0 && size != size_for_thumbnail) /* Don't play missing or truncated clips */
662 { 666 {
663#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR) 667#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
664 bitswap(p_thumbnail, size); 668 bitswap(p_thumbnail, size);
diff --git a/apps/talk.h b/apps/talk.h
index fcb365be75..adf4155836 100644
--- a/apps/talk.h
+++ b/apps/talk.h
@@ -27,7 +27,7 @@
27#include <stdbool.h> 27#include <stdbool.h>
28#include "time.h" 28#include "time.h"
29 29
30#define VOICE_VERSION 300 /* 3.00 - if you change this, change it in voicefont too */ 30#define VOICE_VERSION 400 /* 4.00 - if you change this, change it in voicefont too */
31 31
32enum { 32enum {
33 /* See array "unit_voiced" in talk.c function "talk_value" */ 33 /* See array "unit_voiced" in talk.c function "talk_value" */
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
new file mode 100644
index 0000000000..8792d1c752
--- /dev/null
+++ b/apps/voice_thread.c
@@ -0,0 +1,444 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Michael Sevakis
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "system.h"
20#include "thread.h"
21#include "logf.h"
22#include "voice_thread.h"
23#include "talk.h"
24#include "dsp.h"
25#include "audio.h"
26#include "pcmbuf.h"
27#include "codecs/libspeex/speex/speex.h"
28
29/* Define any of these as "1" to log regular and/or timeout messages */
30#define VOICE_LOGQUEUES 0
31#define VOICE_LOGQUEUES_SYS_TIMEOUT 0
32
33#if VOICE_LOGQUEUES
34#define LOGFQUEUE logf
35#else
36#define LOGFQUEUE(...)
37#endif
38
39#if VOICE_LOGQUEUES_SYS_TIMEOUT
40#define LOGFQUEUE_SYS_TIMEOUT logf
41#else
42#define LOGFQUEUE_SYS_TIMEOUT(...)
43#endif
44
45#ifndef IBSS_ATTR_VOICE_STACK
46#define IBSS_ATTR_VOICE_STACK IBSS_ATTR
47#endif
48
49#define VOICE_FRAME_SIZE 320 /* Samples / frame */
50#define VOICE_SAMPLE_RATE 16000 /* Sample rate in HZ */
51#define VOICE_SAMPLE_DEPTH 16 /* Sample depth in bits */
52
53/* Voice thread variables */
54static struct thread_entry *voice_thread_p = NULL;
55static long voice_stack[0x740/sizeof(long)] IBSS_ATTR_VOICE_STACK;
56static const char voice_thread_name[] = "voice";
57
58/* Voice thread synchronization objects */
59static struct event_queue voice_queue NOCACHEBSS_ATTR;
60static struct mutex voice_mutex NOCACHEBSS_ATTR;
61static struct event voice_event NOCACHEBSS_ATTR;
62static struct queue_sender_list voice_queue_sender_list NOCACHEBSS_ATTR;
63
64/* Buffer for decoded samples */
65static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR;
66
67enum voice_thread_states
68{
69 TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */
70 TSTATE_DECODE, /* Voice is decoding a clip */
71 TSTATE_BUFFER_INSERT, /* Voice is sending decoded audio to PCM */
72};
73
74enum voice_thread_messages
75{
76 Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */
77 Q_VOICE_PLAY, /* Play a clip */
78 Q_VOICE_STOP, /* Stop current clip */
79 Q_VOICE_STATE, /* Query playing state */
80};
81
82/* Structure to store clip data callback info */
83struct voice_info
84{
85 pcm_more_callback_type get_more; /* Callback to get more clips */
86 unsigned char *start; /* Start of clip */
87 ssize_t size; /* Size of clip */
88};
89
90/* Private thread data for its current state that must be passed to its
91 * internal functions */
92struct voice_thread_data
93{
94 int state; /* Thread state (TSTATE_*) */
95 struct queue_event ev; /* Last queue event pulled from queue */
96 void *st; /* Decoder instance */
97 SpeexBits bits; /* Bit cursor */
98 struct dsp_config *dsp; /* DSP used for voice output */
99 struct voice_info vi; /* Copy of clip data */
100 const char *src[2]; /* Current output buffer pointers */
101 int lookahead; /* Number of samples to drop at start of clip */
102 int count; /* Count of samples remaining to send to PCM */
103};
104
105/* Audio playback is in a playing state? */
106static inline bool playback_is_playing(void)
107{
108 return (audio_status() & AUDIO_STATUS_PLAY) != 0;
109}
110
111/* Stop any current clip and start playing a new one */
112void mp3_play_data(const unsigned char* start, int size,
113 pcm_more_callback_type get_more)
114{
115 /* Shared struct to get data to the thread - once it replies, it has
116 * safely cached it in its own private data */
117 static struct voice_info voice_clip NOCACHEBSS_ATTR;
118
119 if (get_more != NULL && start != NULL && size > 0)
120 {
121 mutex_lock(&voice_mutex);
122
123 voice_clip.get_more = get_more;
124 voice_clip.start = (unsigned char *)start;
125 voice_clip.size = size;
126 LOGFQUEUE("mp3 >| voice Q_VOICE_PLAY");
127 queue_send(&voice_queue, Q_VOICE_PLAY, (intptr_t)&voice_clip);
128
129 mutex_unlock(&voice_mutex);
130 }
131}
132
133/* Stop current voice clip from playing */
134void mp3_play_stop(void)
135{
136 mutex_lock(&voice_mutex); /* Sync against voice_stop */
137
138 LOGFQUEUE("mp3 >| voice Q_VOICE_STOP: 1");
139 queue_send(&voice_queue, Q_VOICE_STOP, 1);
140
141 mutex_unlock(&voice_mutex);
142}
143
144void mp3_play_pause(bool play)
145{
146 /* a dummy */
147 (void)play;
148}
149
150/* Tell is voice is still in a playing state */
151bool mp3_is_playing(void)
152{
153 /* TODO: Implement a timeout or state query function for event objects */
154 LOGFQUEUE("mp3 >| voice Q_VOICE_STATE");
155 int state = queue_send(&voice_queue, Q_VOICE_STATE, 0);
156 return state != TSTATE_STOPPED;
157}
158
159/* This function is meant to be used by the buffer request functions to
160 ensure the codec is no longer active */
161void voice_stop(void)
162{
163 mutex_lock(&voice_mutex);
164
165 /* Stop the output and current clip */
166 mp3_play_stop();
167
168 /* Careful if using sync objects in talk.c - make sure locking order is
169 * observed with one or the other always granted first */
170
171 /* Unqueue all future clips */
172 talk_force_shutup();
173
174 mutex_unlock(&voice_mutex);
175} /* voice_stop */
176
177/* Wait for voice to finish speaking. */
178void voice_wait(void)
179{
180 /* NOTE: One problem here is that we can't tell if another thread started a
181 * new clip by the time we wait. This should be resolvable if conditions
182 * ever require knowing the very clip you requested has finished. */
183 event_wait(&voice_event, STATE_SIGNALED);
184}
185
186/* Initialize voice thread data that must be valid upon starting and the
187 * setup the DSP parameters */
188static void voice_data_init(struct voice_thread_data *td)
189{
190 td->state = TSTATE_STOPPED;
191 td->dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP,
192 CODEC_IDX_VOICE);
193
194 dsp_configure(td->dsp, DSP_RESET, 0);
195 dsp_configure(td->dsp, DSP_SET_FREQUENCY, VOICE_SAMPLE_RATE);
196 dsp_configure(td->dsp, DSP_SET_SAMPLE_DEPTH, VOICE_SAMPLE_DEPTH);
197 dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO);
198}
199
200/* Voice thread message processing */
201static void voice_message(struct voice_thread_data *td)
202{
203 while (1)
204 {
205 switch (td->ev.id)
206 {
207 case Q_VOICE_PLAY:
208 LOGFQUEUE("voice < Q_VOICE_PLAY");
209 /* Put up a block for completion signal */
210 event_set_state(&voice_event, STATE_NONSIGNALED);
211
212 /* Copy the clip info */
213 td->vi = *(struct voice_info *)td->ev.data;
214
215 /* Be sure audio buffer is initialized */
216 audio_restore_playback(AUDIO_WANT_VOICE);
217
218 /* We need nothing more from the sending thread - let it run */
219 queue_reply(&voice_queue, 1);
220
221 if (td->state == TSTATE_STOPPED)
222 {
223 /* Boost CPU now */
224 trigger_cpu_boost();
225 }
226 else if (!playback_is_playing())
227 {
228 /* Just voice, stop any clip still playing */
229 pcmbuf_play_stop();
230 }
231
232 /* Clean-start the decoder */
233 td->st = speex_decoder_init(&speex_wb_mode);
234
235 /* Make bit buffer use our own buffer */
236 speex_bits_set_bit_buffer(&td->bits, td->vi.start, td->vi.size);
237 speex_decoder_ctl(td->st, SPEEX_GET_LOOKAHEAD, &td->lookahead);
238
239 td->state = TSTATE_DECODE;
240 return;
241
242 case Q_VOICE_STOP:
243 LOGFQUEUE("voice < Q_VOICE_STOP: %d", ev.data);
244
245 if (td->ev.data != 0 && !playback_is_playing())
246 {
247 /* If not playing, it's just voice so stop pcm playback */
248 pcmbuf_play_stop();
249 }
250
251 /* Cancel boost */
252 sleep(0);
253
254 td->state = TSTATE_STOPPED;
255 event_set_state(&voice_event, STATE_SIGNALED);
256 break;
257
258 case Q_VOICE_STATE:
259 LOGFQUEUE("voice < Q_VOICE_STATE");
260 queue_reply(&voice_queue, td->state);
261
262 if (td->state == TSTATE_STOPPED)
263 break; /* Not in a playback state */
264
265 return;
266
267 default:
268 /* Default messages get a reply and thread continues with no
269 * state transition */
270 LOGFQUEUE("voice < default");
271
272 if (td->state == TSTATE_STOPPED)
273 break; /* Not in playback state */
274
275 queue_reply(&voice_queue, 0);
276 return;
277 }
278
279 queue_wait(&voice_queue, &td->ev);
280 }
281}
282
283/* Voice thread entrypoint */
284static void voice_thread(void)
285{
286 struct voice_thread_data td;
287
288 voice_data_init(&td);
289
290 goto message_wait;
291
292 while (1)
293 {
294 td.state = TSTATE_DECODE;
295
296 if (!queue_empty(&voice_queue))
297 {
298 message_wait:
299 queue_wait(&voice_queue, &td.ev);
300
301 message_process:
302 voice_message(&td);
303
304 /* Branch to initial start point or branch back to previous
305 * operation if interrupted by a message */
306 switch (td.state)
307 {
308 case TSTATE_DECODE: goto voice_decode;
309 case TSTATE_BUFFER_INSERT: goto buffer_insert;
310 default: goto message_wait;
311 }
312 }
313
314 voice_decode:
315 /* Check if all data was exhausted for this clip */
316 if (speex_bits_remaining(&td.bits) < 8)
317 {
318 voice_error:
319 /* Get next clip */
320 td.vi.size = 0;
321
322 if (td.vi.get_more != NULL)
323 td.vi.get_more(&td.vi.start, &td.vi.size);
324
325 if (td.vi.start != NULL && td.vi.size > 0)
326 {
327 /* Make bit buffer use our own buffer */
328 speex_bits_set_bit_buffer(&td.bits, td.vi.start, td.vi.size);
329 speex_decoder_ctl(td.st, SPEEX_GET_LOOKAHEAD, &td.lookahead);
330
331 yield();
332
333 if (!queue_empty(&voice_queue))
334 goto message_wait;
335 else
336 goto voice_decode;
337 }
338
339 /* If all clips are done and not playing, force pcm playback. */
340 if (!pcm_is_playing())
341 pcmbuf_play_start();
342
343 /* Synthesize a stop request */
344 /* NOTE: We have no way to know when the pcm data placed in the
345 * buffer is actually consumed and playback has reached the end
346 * so until the info is available or inferred somehow, this will
347 * not be accurate and the stopped signal will come too soon.
348 * ie. You may not hear the "Shutting Down" splash even though
349 * it waits for voice to stop. */
350 td.ev.id = Q_VOICE_STOP;
351 td.ev.data = 0; /* Let PCM drain by itself */
352 yield();
353 goto message_process;
354 }
355
356 /* Decode the data */
357 int status = speex_decode_int(td.st, &td.bits, voice_output_buf);
358 yield();
359
360 if (status == -2)
361 goto voice_error; /* error - try some more */
362
363 /* Output the decoded frame */
364 td.count = VOICE_FRAME_SIZE - td.lookahead;
365 td.src[0] = (const char *)&voice_output_buf[td.lookahead];
366 td.src[1] = NULL;
367 td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead);
368
369 buffer_insert:
370 /* Process the PCM samples in the DSP and send out for mixing */
371 td.state = TSTATE_BUFFER_INSERT;
372
373 while (td.count > 0)
374 {
375 int out_count = dsp_output_count(td.dsp, td.count);
376 int inp_count;
377 char *dest;
378
379 while (1)
380 {
381 if (!queue_empty(&voice_queue))
382 goto message_wait;
383
384 if ((dest = pcmbuf_request_voice_buffer(&out_count)) != NULL)
385 break;
386
387 yield();
388 }
389
390 /* Get the real input_size for output_size bytes, guarding
391 * against resampling buffer overflows. */
392 inp_count = dsp_input_count(td.dsp, out_count);
393
394 if (inp_count <= 0)
395 break;
396
397 /* Input size has grown, no error, just don't write more than
398 * length */
399 if (inp_count > td.count)
400 inp_count = td.count;
401
402 out_count = dsp_process(td.dsp, dest, td.src, inp_count);
403
404 if (out_count <= 0)
405 break;
406
407 pcmbuf_write_voice_complete(out_count);
408 td.count -= inp_count;
409 }
410
411 yield();
412 } /* end while */
413} /* voice_thread */
414
415/* Initialize all synchronization objects create the thread */
416void voice_thread_init(void)
417{
418 logf("Starting voice thread");
419 queue_init(&voice_queue, false);
420 queue_enable_queue_send(&voice_queue, &voice_queue_sender_list);
421 mutex_init(&voice_mutex);
422 event_init(&voice_event, STATE_SIGNALED | EVENT_MANUAL);
423 voice_thread_p = create_thread(voice_thread, voice_stack,
424 sizeof(voice_stack), CREATE_THREAD_FROZEN,
425 voice_thread_name IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU));
426} /* voice_thread_init */
427
428/* Unfreeze the voice thread */
429void voice_thread_resume(void)
430{
431 logf("Thawing voice thread");
432 thread_thaw(voice_thread_p);
433 /* Wait for initialization to complete (a very short wait until the
434 * voice thread is available to process messages) */
435 queue_send(&voice_queue, Q_VOICE_NULL, 0);
436}
437
438#ifdef HAVE_PRIORITY_SCHEDULING
439/* Set the voice thread priority */
440void voice_thread_set_priority(int priority)
441{
442 thread_set_priority(voice_thread_p, priority);
443}
444#endif
diff --git a/apps/voice_thread.h b/apps/voice_thread.h
new file mode 100644
index 0000000000..72c2054317
--- /dev/null
+++ b/apps/voice_thread.h
@@ -0,0 +1,33 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Michael Sevakis
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef VOICE_THREAD_H
20#define VOICE_THREAD_H
21
22void mp3_play_data(const unsigned char* start, int size,
23 void (*get_more)(unsigned char** start, size_t* size));
24void mp3_play_stop(void);
25void mp3_play_pause(bool play);
26bool mp3_is_playing(void);
27
28void voice_stop(void);
29void voice_thread_init(void);
30void voice_thread_resume(void);
31void voice_thread_set_priority(int priority);
32
33#endif /* VOICE_THREAD_H */
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 9f3b490e5e..59179961ee 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -282,7 +282,6 @@ target/coldfire/crt0.S
282target/coldfire/memcpy-coldfire.S 282target/coldfire/memcpy-coldfire.S
283target/coldfire/memmove-coldfire.S 283target/coldfire/memmove-coldfire.S
284target/coldfire/memset-coldfire.S 284target/coldfire/memset-coldfire.S
285target/coldfire/memswap128-coldfire.S
286target/coldfire/strlen-coldfire.S 285target/coldfire/strlen-coldfire.S
287#if defined(HAVE_LCD_COLOR) \ 286#if defined(HAVE_LCD_COLOR) \
288 || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED) 287 || defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED)
@@ -305,7 +304,6 @@ common/strlen.c
305#ifndef SIMULATOR 304#ifndef SIMULATOR
306target/arm/memset-arm.S 305target/arm/memset-arm.S
307target/arm/memset16-arm.S 306target/arm/memset16-arm.S
308target/arm/memswap128-arm.S
309#if CONFIG_I2C == I2C_PP5024 || CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002 307#if CONFIG_I2C == I2C_PP5024 || CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002
310target/arm/i2c-pp.c 308target/arm/i2c-pp.c
311#elif CONFIG_I2C == I2C_PNX0101 309#elif CONFIG_I2C == I2C_PNX0101
@@ -350,7 +348,6 @@ common/memcpy.c
350common/memmove.c 348common/memmove.c
351common/memset.c 349common/memset.c
352common/memset16.c 350common/memset16.c
353common/memswap128.c
354common/strlen.c 351common/strlen.c
355#ifndef SIMULATOR 352#ifndef SIMULATOR
356crt0.S 353crt0.S
diff --git a/firmware/common/memswap128.c b/firmware/common/memswap128.c
deleted file mode 100644
index af1fe157b6..0000000000
--- a/firmware/common/memswap128.c
+++ /dev/null
@@ -1,44 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Michael Sevakis
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "config.h"
20#include <string.h>
21#include <inttypes.h>
22
23void memswap128(void *a, void *b, size_t len)
24{
25 for (len >>= 4; len > 0; len--, a += 16, b += 16)
26 {
27 int32_t a0 = *((int32_t *)a + 0);
28 int32_t a1 = *((int32_t *)a + 1);
29 int32_t a2 = *((int32_t *)a + 2);
30 int32_t a3 = *((int32_t *)a + 3);
31 int32_t b0 = *((int32_t *)b + 0);
32 int32_t b1 = *((int32_t *)b + 1);
33 int32_t b2 = *((int32_t *)b + 2);
34 int32_t b3 = *((int32_t *)b + 3);
35 *((int32_t *)b + 0) = a0;
36 *((int32_t *)b + 1) = a1;
37 *((int32_t *)b + 2) = a2;
38 *((int32_t *)b + 3) = a3;
39 *((int32_t *)a + 0) = b0;
40 *((int32_t *)a + 1) = b1;
41 *((int32_t *)a + 2) = b2;
42 *((int32_t *)a + 3) = b3;
43 }
44}
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 468856a87a..061b2230f4 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -109,10 +109,16 @@ void audio_beep(int duration);
109void audio_init_playback(void); 109void audio_init_playback(void);
110/* Required call when audio buffer is require for some other purpose */ 110/* Required call when audio buffer is require for some other purpose */
111unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size); 111unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size);
112#ifdef IRAM_STEAL 112/* Stops audio from serving playback */
113/* Required call when codec IRAM is needed for some other purpose */ 113void audio_hard_stop(void);
114void audio_iram_steal(void); 114/* Retores the audio buffer to handle the requested playback */
115#endif 115enum
116{
117 AUDIO_WANT_PLAYBACK = 0,
118 AUDIO_WANT_VOICE,
119};
120
121bool audio_restore_playback(int type);
116 122
117/* channel modes */ 123/* channel modes */
118enum rec_channel_modes 124enum rec_channel_modes
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 48dc3f5693..6a2c02cffd 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -383,7 +383,7 @@
383#define IBSS_ATTR __attribute__ ((section(".ibss"))) 383#define IBSS_ATTR __attribute__ ((section(".ibss")))
384#define USE_IRAM 384#define USE_IRAM
385#if CONFIG_CPU != SH7034 385#if CONFIG_CPU != SH7034
386#define IRAM_STEAL 386#define PLUGIN_USE_IRAM
387#endif 387#endif
388#if defined(CPU_ARM) 388#if defined(CPU_ARM)
389/* GCC quirk workaround: arm-elf-gcc treats static functions as short_call 389/* GCC quirk workaround: arm-elf-gcc treats static functions as short_call
diff --git a/firmware/target/arm/memswap128-arm.S b/firmware/target/arm/memswap128-arm.S
deleted file mode 100644
index f672def1ec..0000000000
--- a/firmware/target/arm/memswap128-arm.S
+++ /dev/null
@@ -1,44 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Michael Sevakis
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/****************************************************************************
21 * void memswap128(void *buf1, void *buf2, size_t len)
22 */
23 .section .icode, "ax", %progbits
24 .align 2
25 .global memswap128
26 .type memswap128, %function
27memswap128:
28 @ r0 = buf1
29 @ r1 = buf2
30 @ r2 = len
31 movs r2, r2, lsr #4 @ bytes => lines, len == 0?
32 moveq pc, lr @ not at least a line? leave
33 stmdb sp!, { r4-r10, lr } @ save registers and return address
34.loop: @
35 ldmia r0, { r3-r6 } @ read four longwords from buf1
36 ldmia r1, { r7-r10 } @ read four longwords from buf2
37 stmia r0!, { r7-r10 } @ write buf2 data to buf1, buf1 += 16
38 stmia r1!, { r3-r6 } @ write buf1 data to buf2, buf2 += 16
39 subs r2, r2, #1 @ len -= 1, len > 0 ?
40 bhi .loop @ yes? keep exchanging
41 ldmia sp!, { r4-r10, pc } @ restore registers and return
42.end:
43 .size memswap128, .end-memswap128
44
diff --git a/firmware/target/coldfire/memswap128-coldfire.S b/firmware/target/coldfire/memswap128-coldfire.S
deleted file mode 100644
index 5de628dabd..0000000000
--- a/firmware/target/coldfire/memswap128-coldfire.S
+++ /dev/null
@@ -1,50 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 by Michael Sevakis
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/****************************************************************************
21 * void memswap128(void *buf1, void *buf2, size_t len)
22 */
23 .section .icode, "ax", @progbits
24 .align 2
25 .global memswap128
26 .type memswap128, @function
27memswap128:
28 lea.l -28(%sp), %sp | save registers
29 movem.l %d2-%d7/%a2, (%sp) |
30 movem.l 32(%sp), %a0-%a2 | %a0 = buf1
31 | %a1 = buf2
32 | %a2 = len
33 lea.l -15(%a0, %a2.l), %a2 | %a2 = end address - 15
34 cmp.l %a0, %a2 | end address <= buf1?
35 bls.b .no_lines | not at least a line? leave
36.loop: |
37 movem.l (%a0), %d0-%d3 | read four longwords from buf1
38 movem.l (%a1), %d4-%d7 | read four longwords from buf2
39 movem.l %d4-%d7, (%a0) | write buf2 data to buf1
40 movem.l %d0-%d3, (%a1) | write buf1 data to buf2
41 lea.l 16(%a0), %a0 | buf1 += 16
42 lea.l 16(%a1), %a1 | buf2 += 16
43 cmp.l %a0, %a2 | %a0 < %d0?
44 bhi.b .loop | yes? keep exchanging
45.no_lines: |
46 movem.l (%sp), %d2-%d7/%a2 | restore registers
47 lea.l 28(%sp), %sp |
48 rts |
49.end:
50 .size memswap128, .end-memswap128
diff --git a/tools/voicefont.c b/tools/voicefont.c
index 94f8252e6e..6fd443a63f 100644
--- a/tools/voicefont.c
+++ b/tools/voicefont.c
@@ -178,7 +178,7 @@ int main (int argc, char** argv)
178 /* Create the file format: */ 178 /* Create the file format: */
179 179
180 /* 1st 32 bit value in the file is the version number */ 180 /* 1st 32 bit value in the file is the version number */
181 value = SWAP4(300); /* 3.00 */ 181 value = SWAP4(400); /* 4.00 */
182 fwrite(&value, sizeof(value), 1, pFile); 182 fwrite(&value, sizeof(value), 1, pFile);
183 183
184 /* 2nd 32 bit value in the file is the id number for the target 184 /* 2nd 32 bit value in the file is the id number for the target