diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-11-18 17:12:19 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-11-18 17:12:19 +0000 |
commit | 99617d71bad0e5870a38e37c8654e46868e2a5ba (patch) | |
tree | e0ecc3b73e8e167c7f5bf00a6c88b83c1119aea3 | |
parent | 75432619e8be2f22f86ed0869d46bf7245c7c14d (diff) | |
download | rockbox-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
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 |
16 | menus/eq_menu.c | 16 | menus/eq_menu.c |
17 | buffering.c | 17 | buffering.c |
18 | voice_thread.c | ||
18 | #endif | 19 | #endif |
19 | menus/main_menu.c | 20 | menus/main_menu.c |
20 | menus/playback_menu.c | 21 | menus/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 | ||
68 | extern void* plugin_get_audio_buffer(size_t *buffer_size); | 68 | extern void* plugin_get_audio_buffer(size_t *buffer_size); |
69 | 69 | ||
70 | struct codec_api ci_voice; | ||
71 | |||
72 | struct codec_api ci = { | 70 | struct 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 | ||
168 | void codec_get_full_path(char *path, const char *codec_root_fn) | 168 | void 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 | ||
48 | void speex_bits_init(SpeexBits *bits) | 49 | void 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 | ||
61 | void speex_bits_init_buffer(SpeexBits *bits, void *buff, int buf_size) | 63 | void 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 | ||
85 | void speex_bits_destroy(SpeexBits *bits) | 88 | void 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 | ||
92 | void speex_bits_reset(SpeexBits *bits) | 96 | void 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) |
110 | void speex_bits_read_from(SpeexBits *bits, char *chars, int len) | 114 | void 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 | ||
35 | const signed char exc_10_16_table[160] ICONST_ATTR = { | 35 | const signed char exc_10_16_table[160] EXC_ICONST_ATTR = { |
36 | 22,39,14,44,11,35,-2,23,-4,6, | 36 | 22,39,14,44,11,35,-2,23,-4,6, |
37 | 46,-28,13,-27,-23,12,4,20,-5,9, | 37 | 46,-28,13,-27,-23,12,4,20,-5,9, |
38 | 37,-18,-23,23,0,9,-6,-20,4,-1, | 38 | 37,-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 | ||
35 | const signed char exc_10_32_table[320] ICONST_ATTR = { | 35 | const signed char exc_10_32_table[320] EXC_ICONST_ATTR = { |
36 | 7,17,17,27,25,22,12,4,-3,0, | 36 | 7,17,17,27,25,22,12,4,-3,0, |
37 | 28,-36,39,-24,-15,3,-9,15,-5,10, | 37 | 28,-36,39,-24,-15,3,-9,15,-5,10, |
38 | 31,-28,11,31,-21,9,-11,-11,-2,-7, | 38 | 31,-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 | ||
35 | const signed char exc_20_32_table[640] ICONST_ATTR = { | 35 | const signed char exc_20_32_table[640] EXC_ICONST_ATTR = { |
36 | 12,32,25,46,36,33,9,14,-3,6,1,-8,0,-10,-5,-7,-7,-7,-5,-5, | 36 | 12,32,25,46,36,33,9,14,-3,6,1,-8,0,-10,-5,-7,-7,-7,-5,-5, |
37 | 31,-27,24,-32,-4,10,-11,21,-3,19,23,-9,22,24,-10,-1,-10,-13,-7,-11, | 37 | 31,-27,24,-32,-4,10,-11,21,-3,19,23,-9,22,24,-10,-1,-10,-13,-7,-11, |
38 | 42,-33,31,19,-8,0,-10,-16,1,-21,-17,10,-8,14,8,4,11,-2,5,-2, | 38 | 42,-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 | ||
35 | const signed char exc_5_256_table[1280] ICONST_ATTR = { | 35 | const signed char exc_5_256_table[1280] EXC_ICONST_ATTR = { |
36 | -8,-37,5,-43,5, | 36 | -8,-37,5,-43,5, |
37 | 73,61,39,12,-3, | 37 | 73,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 | ||
35 | const signed char exc_5_64_table[320] ICONST_ATTR = { | 35 | const signed char exc_5_64_table[320] EXC_ICONST_ATTR = { |
36 | 1,5,-15,49,-66, | 36 | 1,5,-15,49,-66, |
37 | -48,-4,50,-44,7, | 37 | -48,-4,50,-44,7, |
38 | 37,16,-18,25,-26, | 38 | 37,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 | ||
35 | const signed char exc_8_128_table[1024] ICONST_ATTR = { | 35 | const 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, |
38 | 6,20,13,6,8,-22,16,34, | 38 | 6,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 */ |
190 | static struct eq_state eq_data; /* A/V */ | 195 | static struct eq_state eq_data; /* A */ |
196 | |||
197 | /* Software tone controls */ | ||
191 | #ifdef HAVE_SW_TONE_CONTROLS | 198 | #ifdef HAVE_SW_TONE_CONTROLS |
192 | static int prescale; | 199 | static int prescale; /* A/V */ |
193 | static int bass; | 200 | static int bass; /* A/V */ |
194 | static int treble; | 201 | static int treble; /* A/V */ |
195 | /* Filter struct for software bass/treble controls */ | ||
196 | static 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; |
204 | static bool dither_enabled; | 209 | static bool dither_enabled; |
205 | static bool eq_enabled IBSS_ATTR; | ||
206 | static long eq_precut; | 210 | static long eq_precut; |
207 | static long track_gain; | 211 | static long track_gain; |
208 | static bool new_gain; | 212 | static bool new_gain; |
@@ -212,9 +216,8 @@ static long album_peak; | |||
212 | static long replaygain; | 216 | static long replaygain; |
213 | static bool crossfeed_enabled; | 217 | static 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]) |
217 | static 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; | |||
224 | int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR; | 227 | int32_t sample_buf[SAMPLE_BUF_COUNT] IBSS_ATTR; |
225 | static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR; | 228 | static int32_t resample_buf[RESAMPLE_BUF_COUNT] IBSS_ATTR; |
226 | 229 | ||
227 | /* set a new dsp and return old one */ | ||
228 | static 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 */ |
237 | static inline long clip_sample(int32_t sample, int32_t min, int32_t range) | 232 | static inline long clip_sample(int32_t sample, int32_t min, int32_t range) |
@@ -263,8 +258,8 @@ int sound_get_pitch(void) | |||
263 | void sound_set_pitch(int permille) | 258 | void 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 | */ |
389 | static void sample_input_new_format(void) | 384 | static 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 | */ |
533 | static void sample_output_new_format(void) | 528 | static 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 | ||
641 | static void resampler_new_delta(void) | 636 | static 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 | */ |
666 | static inline int resample(int count, int32_t *src[]) | 661 | static 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 | ||
682 | static void dither_init(void) | 677 | static 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 | ||
693 | void dsp_dither_enable(bool enable) | 684 | void 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[]) | |||
762 | void dsp_set_crossfeed(bool enable) | 751 | void 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 | ||
770 | void dsp_set_crossfeed_direct_gain(int gain) | 758 | void 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 | */ | ||
861 | void 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) | |||
871 | void dsp_set_eq_precut(int precut) | 849 | void 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 | */ | ||
929 | void dsp_set_eq(bool enable) | ||
930 | { | ||
931 | audio_dsp.eq_process = enable ? eq_process : NULL; | ||
932 | set_gain(&audio_dsp); | ||
933 | } | ||
934 | |||
947 | void dsp_set_stereo_width(int value) | 935 | void 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 | ||
972 | static 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 | */ | ||
983 | int 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 | ||
1058 | static 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 | */ | ||
1073 | int 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 | */ |
1117 | int dsp_process(char *dst, const char *src[], int count) | 1111 | int 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 */ |
1190 | int dsp_output_count(int count) | 1178 | int 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 | */ |
1212 | int dsp_input_count(int count) | 1200 | int 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 | ||
1228 | int dsp_stereo_mode(void) | ||
1229 | { | ||
1230 | return dsp->stereo_mode; | ||
1231 | } | ||
1232 | |||
1233 | static void dsp_set_gain_var(long *var, long value) | 1216 | static 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 | ||
1243 | static void dsp_update_functions(void) | 1222 | static 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 | ||
1251 | bool dsp_configure(int setting, intptr_t value) | 1230 | intptr_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 | ||
35 | enum | 35 | enum |
36 | { | 36 | { |
37 | CODEC_IDX_AUDIO = 0, | ||
38 | CODEC_IDX_VOICE, | ||
39 | }; | ||
40 | |||
41 | enum | ||
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 | ||
204 | int dsp_process(char *dest, const char *src[], int count); | 210 | struct dsp_config; |
205 | int dsp_input_count(int count); | 211 | |
206 | int dsp_output_count(int count); | 212 | int dsp_process(struct dsp_config *dsp, char *dest, |
207 | int dsp_stereo_mode(void); | 213 | const char *src[], int count); |
208 | bool dsp_configure(int setting, intptr_t value); | 214 | int dsp_input_count(struct dsp_config *dsp, int count); |
215 | int dsp_output_count(struct dsp_config *dsp, int count); | ||
216 | intptr_t dsp_configure(struct dsp_config *dsp, int setting, | ||
217 | intptr_t value); | ||
209 | void dsp_set_replaygain(void); | 218 | void dsp_set_replaygain(void); |
210 | void dsp_set_crossfeed(bool enable); | 219 | void dsp_set_crossfeed(bool enable); |
211 | void dsp_set_crossfeed_direct_gain(int gain); | 220 | void dsp_set_crossfeed_direct_gain(int gain); |
212 | void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, long cutoff); | 221 | void dsp_set_crossfeed_cross_params(long lf_gain, long hf_gain, |
222 | long cutoff); | ||
213 | void dsp_set_eq(bool enable); | 223 | void dsp_set_eq(bool enable); |
214 | void dsp_set_eq_precut(int precut); | 224 | void dsp_set_eq_precut(int precut); |
215 | void dsp_set_eq_coefs(int band); | 225 | void dsp_set_eq_coefs(int band); |
216 | void sound_set_pitch(int r); | 226 | void sound_set_pitch(int r); |
217 | int sound_get_pitch(void); | 227 | int sound_get_pitch(void); |
218 | int dsp_callback(int msg, intptr_t param); | 228 | int dsp_callback(int msg, intptr_t param); |
219 | void dsp_set_channel_config(int value); | ||
220 | void dsp_set_stereo_width(int value); | ||
221 | void dsp_dither_enable(bool enable); | 229 | void 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 |
252 | static void boost_codec_thread(bool boost) | 253 | static 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 | ||
878 | void* pcmbuf_request_voice_buffer(int *count, bool mix) | 886 | void * 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 | ||
900 | bool pcmbuf_is_crossfade_active(void) | 913 | bool pcmbuf_is_crossfade_active(void) |
@@ -1030,8 +1043,15 @@ int pcmbuf_mix_free(void) | |||
1030 | return 100; | 1043 | return 100; |
1031 | } | 1044 | } |
1032 | 1045 | ||
1033 | void pcmbuf_mix_voice(int count) | 1046 | void 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)); | |||
67 | size_t pcmbuf_free(void); | 67 | size_t pcmbuf_free(void); |
68 | unsigned int pcmbuf_get_latency(void); | 68 | unsigned int pcmbuf_get_latency(void); |
69 | void pcmbuf_set_low_latency(bool state); | 69 | void pcmbuf_set_low_latency(bool state); |
70 | void * pcmbuf_request_buffer(int *count); | ||
70 | void pcmbuf_write_complete(int count); | 71 | void pcmbuf_write_complete(int count); |
71 | void* pcmbuf_request_buffer(int *count); | 72 | void * pcmbuf_request_voice_buffer(int *count); |
72 | void* pcmbuf_request_voice_buffer(int *count, bool mix); | 73 | void pcmbuf_write_voice_complete(int count); |
73 | bool pcmbuf_is_crossfade_enabled(void); | 74 | bool pcmbuf_is_crossfade_enabled(void); |
74 | void pcmbuf_crossfade_enable(bool on_off); | 75 | void pcmbuf_crossfade_enable(bool on_off); |
75 | void pcmbuf_crossfade_enable_finished(void); | 76 | void pcmbuf_crossfade_enable_finished(void); |
76 | int pcmbuf_usage(void); | 77 | int pcmbuf_usage(void); |
77 | int pcmbuf_mix_free(void); | 78 | int pcmbuf_mix_free(void); |
78 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); | 79 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude); |
79 | void pcmbuf_mix_voice(int count); | ||
80 | 80 | ||
81 | int pcmbuf_used_descs(void); | 81 | int pcmbuf_used_descs(void); |
82 | int pcmbuf_descs(void); | 82 | int 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 | |||
182 | bool audio_is_initialized = false; | 175 | bool 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; | |||
267 | static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */ | 260 | static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */ |
268 | 261 | ||
269 | /* Multiple threads */ | 262 | /* Multiple threads */ |
270 | static 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 */ |
272 | static void set_filebuf_watermark(int seconds, size_t max); | 264 | static void set_filebuf_watermark(int seconds, size_t max); |
273 | 265 | ||
@@ -291,68 +283,6 @@ IBSS_ATTR; | |||
291 | static const char codec_thread_name[] = "codec"; | 283 | static const char codec_thread_name[] = "codec"; |
292 | struct thread_entry *codec_thread_p; /* For modifying thread priority later. */ | 284 | struct thread_entry *codec_thread_p; /* For modifying thread priority later. */ |
293 | 285 | ||
294 | static volatile int current_codec IDATA_ATTR; /* Current codec (normal/voice) */ | ||
295 | |||
296 | /* Voice thread */ | ||
297 | #ifdef PLAYBACK_VOICE | ||
298 | |||
299 | extern struct codec_api ci_voice; | ||
300 | |||
301 | static struct thread_entry *voice_thread_p = NULL; | ||
302 | static struct event_queue voice_queue NOCACHEBSS_ATTR; | ||
303 | static long voice_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] | ||
304 | IBSS_ATTR_VOICE_STACK; | ||
305 | static const char voice_thread_name[] = "voice codec"; | ||
306 | |||
307 | /* Voice codec swapping control */ | ||
308 | extern unsigned char codecbuf[]; /* DRAM codec swap buffer */ | ||
309 | |||
310 | #ifdef SIMULATOR | ||
311 | /* IRAM codec swap buffer for sim*/ | ||
312 | static 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 */ | ||
319 | static unsigned char *iram_buf = NULL; | ||
320 | /* Pointer to DRAM buffer for codec swapping */ | ||
321 | static 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 | */ | ||
326 | static bool swap_codec_parity NOCACHEBSS_ATTR = false; /* true=odd, false=even */ | ||
327 | /* Locking to control which codec (normal/voice) is running */ | ||
328 | static struct semaphore sem_codecthread NOCACHEBSS_ATTR; | ||
329 | static struct event event_codecthread NOCACHEBSS_ATTR; | ||
330 | |||
331 | /* Voice state */ | ||
332 | static volatile bool voice_thread_start = false; /* Triggers voice playback (A/V) */ | ||
333 | static volatile bool voice_is_playing NOCACHEBSS_ATTR = false; /* Is voice currently playing? (V) */ | ||
334 | static volatile bool voice_codec_loaded NOCACHEBSS_ATTR = false; /* Is voice codec loaded (V/A-) */ | ||
335 | static unsigned char *voicebuf = NULL; | ||
336 | static size_t voice_remaining = 0; | ||
337 | |||
338 | #ifdef IRAM_STEAL | ||
339 | /* Voice IRAM has been stolen for other use */ | ||
340 | static bool voice_iram_stolen = false; | ||
341 | #endif | ||
342 | |||
343 | static void (*voice_getmore)(unsigned char** start, size_t* size) = NULL; | ||
344 | |||
345 | struct voice_info { | ||
346 | void (*callback)(unsigned char **start, size_t* size); | ||
347 | size_t size; | ||
348 | unsigned char *buf; | ||
349 | }; | ||
350 | static void voice_thread(void); | ||
351 | static void voice_stop(void); | ||
352 | |||
353 | #endif /* PLAYBACK_VOICE */ | ||
354 | |||
355 | |||
356 | /* --- Helper functions --- */ | 286 | /* --- Helper functions --- */ |
357 | 287 | ||
358 | static struct mp3entry *bufgetid3(int handle_id) | 288 | static 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 | ||
418 | void 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 | |||
439 | void 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 | |||
448 | void mp3_play_pause(bool play) | ||
449 | { | ||
450 | /* a dummy */ | ||
451 | (void)play; | ||
452 | } | ||
453 | |||
454 | bool 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 | */ | ||
466 | static 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 */ |
478 | static void audio_hard_stop(void) | 350 | void 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 | |||
360 | bool 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 | ||
485 | unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) | 377 | unsigned 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 | ||
547 | void 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 |
581 | unsigned char *audio_get_recording_buffer(size_t *buffer_size) | 435 | unsigned 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 --- */ |
955 | static 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 | ||
962 | static 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 */ | ||
1047 | static 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. */ | ||
1068 | static 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. */ | ||
1079 | void voice_wait(void) | ||
1080 | { | ||
1081 | #ifdef PLAYBACK_VOICE | ||
1082 | while (is_voice_speaking()) | ||
1083 | sleep(HZ/10); | ||
1084 | #endif | ||
1085 | } | ||
1086 | 790 | ||
1087 | static void set_filebuf_watermark(int seconds, size_t max) | 791 | static 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 | |||
1134 | static 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 | |||
1186 | static 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 | |||
1194 | static void voice_set_elapsed_callback(unsigned int value) | ||
1195 | { | ||
1196 | (void)value; | ||
1197 | } | ||
1198 | |||
1199 | static void voice_set_offset_callback(size_t value) | ||
1200 | { | ||
1201 | (void)value; | ||
1202 | } | ||
1203 | |||
1204 | static 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 | |||
1212 | static 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 */ | ||
1221 | static 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 | |||
1252 | static 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 | |||
1338 | voice_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 | |||
1364 | static void voice_advance_buffer_callback(size_t amount) | ||
1365 | { | ||
1366 | amount = MIN(amount, voice_remaining); | ||
1367 | voicebuf += amount; | ||
1368 | voice_remaining -= amount; | ||
1369 | } | ||
1370 | |||
1371 | static 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 | |||
1378 | static off_t voice_mp3_get_filepos_callback(int newtime) | ||
1379 | { | ||
1380 | (void)newtime; | ||
1381 | |||
1382 | return 0; | ||
1383 | } | ||
1384 | |||
1385 | static void voice_do_nothing(void) | ||
1386 | { | ||
1387 | return; | ||
1388 | } | ||
1389 | |||
1390 | static bool voice_seek_buffer_callback(size_t newpos) | ||
1391 | { | ||
1392 | (void)newpos; | ||
1393 | |||
1394 | return false; | ||
1395 | } | ||
1396 | |||
1397 | static bool voice_request_next_track_callback(void) | ||
1398 | { | ||
1399 | ci_voice.new_track = 0; | ||
1400 | return true; | ||
1401 | } | ||
1402 | |||
1403 | static 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 --- */ |
1427 | static bool codec_pcmbuf_insert_callback( | 834 | static 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 | */ |
3254 | void audio_init(void) | 2520 | void 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 */ |
737 | void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size, | 737 | void 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 | ||
695 | int plugin_load(const char* plugin, void* parameter); | 695 | int plugin_load(const char* plugin, void* parameter); |
696 | void* plugin_get_buffer(size_t *buffer_size); | 696 | void* plugin_get_buffer(size_t *buffer_size); |
697 | void* plugin_get_audio_buffer(size_t *buffer_size); | 697 | void* plugin_get_audio_buffer(size_t *buffer_size); |
698 | #ifdef IRAM_STEAL | 698 | #ifdef PLUGIN_USE_IRAM |
699 | void plugin_iram_init(char *iramstart, char *iramcopy, size_t iram_size, | 699 | void 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 | ||
32 | enum { | 32 | enum { |
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 */ | ||
54 | static struct thread_entry *voice_thread_p = NULL; | ||
55 | static long voice_stack[0x740/sizeof(long)] IBSS_ATTR_VOICE_STACK; | ||
56 | static const char voice_thread_name[] = "voice"; | ||
57 | |||
58 | /* Voice thread synchronization objects */ | ||
59 | static struct event_queue voice_queue NOCACHEBSS_ATTR; | ||
60 | static struct mutex voice_mutex NOCACHEBSS_ATTR; | ||
61 | static struct event voice_event NOCACHEBSS_ATTR; | ||
62 | static struct queue_sender_list voice_queue_sender_list NOCACHEBSS_ATTR; | ||
63 | |||
64 | /* Buffer for decoded samples */ | ||
65 | static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR; | ||
66 | |||
67 | enum 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 | |||
74 | enum 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 */ | ||
83 | struct 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 */ | ||
92 | struct 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? */ | ||
106 | static 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 */ | ||
112 | void 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 */ | ||
134 | void 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 | |||
144 | void mp3_play_pause(bool play) | ||
145 | { | ||
146 | /* a dummy */ | ||
147 | (void)play; | ||
148 | } | ||
149 | |||
150 | /* Tell is voice is still in a playing state */ | ||
151 | bool 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 */ | ||
161 | void 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. */ | ||
178 | void 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 */ | ||
188 | static 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 */ | ||
201 | static 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 */ | ||
284 | static 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 */ | ||
416 | void 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 */ | ||
429 | void 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 */ | ||
440 | void 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 | |||
22 | void mp3_play_data(const unsigned char* start, int size, | ||
23 | void (*get_more)(unsigned char** start, size_t* size)); | ||
24 | void mp3_play_stop(void); | ||
25 | void mp3_play_pause(bool play); | ||
26 | bool mp3_is_playing(void); | ||
27 | |||
28 | void voice_stop(void); | ||
29 | void voice_thread_init(void); | ||
30 | void voice_thread_resume(void); | ||
31 | void 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 | |||
282 | target/coldfire/memcpy-coldfire.S | 282 | target/coldfire/memcpy-coldfire.S |
283 | target/coldfire/memmove-coldfire.S | 283 | target/coldfire/memmove-coldfire.S |
284 | target/coldfire/memset-coldfire.S | 284 | target/coldfire/memset-coldfire.S |
285 | target/coldfire/memswap128-coldfire.S | ||
286 | target/coldfire/strlen-coldfire.S | 285 | target/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 |
306 | target/arm/memset-arm.S | 305 | target/arm/memset-arm.S |
307 | target/arm/memset16-arm.S | 306 | target/arm/memset16-arm.S |
308 | target/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 |
310 | target/arm/i2c-pp.c | 308 | target/arm/i2c-pp.c |
311 | #elif CONFIG_I2C == I2C_PNX0101 | 309 | #elif CONFIG_I2C == I2C_PNX0101 |
@@ -350,7 +348,6 @@ common/memcpy.c | |||
350 | common/memmove.c | 348 | common/memmove.c |
351 | common/memset.c | 349 | common/memset.c |
352 | common/memset16.c | 350 | common/memset16.c |
353 | common/memswap128.c | ||
354 | common/strlen.c | 351 | common/strlen.c |
355 | #ifndef SIMULATOR | 352 | #ifndef SIMULATOR |
356 | crt0.S | 353 | crt0.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 | |||
23 | void 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); | |||
109 | void audio_init_playback(void); | 109 | void 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 */ |
111 | unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size); | 111 | unsigned 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 */ | 113 | void audio_hard_stop(void); |
114 | void audio_iram_steal(void); | 114 | /* Retores the audio buffer to handle the requested playback */ |
115 | #endif | 115 | enum |
116 | { | ||
117 | AUDIO_WANT_PLAYBACK = 0, | ||
118 | AUDIO_WANT_VOICE, | ||
119 | }; | ||
120 | |||
121 | bool audio_restore_playback(int type); | ||
116 | 122 | ||
117 | /* channel modes */ | 123 | /* channel modes */ |
118 | enum rec_channel_modes | 124 | enum 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 | ||
27 | memswap128: | ||
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 | ||
27 | memswap128: | ||
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 |