summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES3
-rw-r--r--apps/action.c2
-rw-r--r--apps/beep.c142
-rw-r--r--apps/gui/wps.c16
-rw-r--r--apps/misc.c5
-rw-r--r--apps/misc.h3
-rw-r--r--apps/pcmbuf.c319
-rw-r--r--apps/pcmbuf.h3
-rw-r--r--apps/playback.c20
-rw-r--r--apps/plugin.c5
-rw-r--r--apps/plugin.h10
-rw-r--r--apps/plugins/fft/fft.c13
-rw-r--r--apps/recorder/keyboard.c2
-rw-r--r--apps/voice_thread.c279
-rw-r--r--apps/voice_thread.h5
15 files changed, 468 insertions, 359 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index c122427900..075ca9a563 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -170,6 +170,9 @@ codec_thread.c
170playback.c 170playback.c
171codecs.c 171codecs.c
172dsp.c 172dsp.c
173#ifndef HAVE_HARDWARE_BEEP
174beep.c
175#endif
173#ifdef HAVE_PITCHSCREEN 176#ifdef HAVE_PITCHSCREEN
174tdspeed.c 177tdspeed.c
175#endif 178#endif
diff --git a/apps/action.c b/apps/action.c
index eb5950bb70..aa19403703 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -205,7 +205,7 @@ static int get_action_worker(int context, int timeout,
205 /* Produce keyclick */ 205 /* Produce keyclick */
206 if (global_settings.keyclick && !(button & BUTTON_REL)) 206 if (global_settings.keyclick && !(button & BUTTON_REL))
207 if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats) 207 if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats)
208 pcmbuf_beep(4000, KEYCLICK_DURATION, 2500*global_settings.keyclick); 208 beep_play(4000, KEYCLICK_DURATION, 2500*global_settings.keyclick);
209#endif 209#endif
210 210
211 if ((context != last_context) && ((last_button & BUTTON_REL) == 0) 211 if ((context != last_context) && ((last_button & BUTTON_REL) == 0)
diff --git a/apps/beep.c b/apps/beep.c
new file mode 100644
index 0000000000..716847263e
--- /dev/null
+++ b/apps/beep.c
@@ -0,0 +1,142 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2011 Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include "system.h"
23#include "settings.h"
24#include "dsp.h"
25#include "pcm.h"
26#include "pcm_mixer.h"
27#include "misc.h"
28
29static int32_t beep_phase; /* Phase of square wave generator */
30static uint32_t beep_step; /* Step of square wave generator on each sample */
31static uint32_t beep_amplitude; /* Amplitude of square wave generator */
32static int beep_count; /* Number of samples remaining to generate */
33
34/* Reserve enough static space for keyclick to fit */
35#define BEEP_BUF_COUNT (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION)
36static uint32_t beep_buf[BEEP_BUF_COUNT] IBSS_ATTR;
37
38/* Actually output samples into beep_buf */
39#if defined(CPU_ARM)
40static FORCE_INLINE void beep_generate(int count)
41{
42 uint32_t *out = beep_buf;
43 uint32_t s;
44
45 asm volatile (
46 "1: \n"
47 "eor %3, %5, %1, asr #31 \n"
48 "subs %2, %2, #1 \n"
49 "str %3, [%0], #4 \n"
50 "add %1, %1, %4 \n"
51 "bgt 1b \n"
52 : "+r"(out), "+r"(beep_phase), "+r"(count),
53 "=&r"(s)
54 : "r"(beep_step), "r"(beep_amplitude));
55}
56#elif defined (CPU_COLDFIRE)
57static FORCE_INLINE void beep_generate(int count)
58{
59 uint32_t *out = beep_buf;
60 uint32_t s;
61
62 asm volatile (
63 "1: \n"
64 "move.l %1, %3 \n"
65 "add.l %4, %1 \n"
66 "add.l %3, %3 \n"
67 "subx.l %3, %3 \n"
68 "eor.l %5, %3 \n"
69 "move.l %3, (%0)+ \n"
70 "subq.l #1, %2 \n"
71 "bgt.b 1b \n"
72 : "+a"(out), "+d"(beep_phase), "+d"(count),
73 "=&d"(s)
74 : "r"(beep_step), "d"(beep_amplitude));
75}
76#else
77static FORCE_INLINE void beep_generate(int count)
78{
79 uint32_t *out = beep_buf;
80 uint32_t amplitude = beep_amplitude;
81 uint32_t step = beep_step;
82 int32_t phase = beep_phase;
83
84 do
85 {
86 *out++ = (phase >> 31) ^ amplitude;
87 phase += step;
88 }
89 while (--count > 0);
90
91 beep_phase = phase;
92}
93#endif
94
95/* Callback to generate the beep frames - also don't want inlining of
96 call below in beep_play */
97static void __attribute__((noinline)) ICODE_ATTR
98beep_get_more(unsigned char **start, size_t *size)
99{
100 int count = beep_count;
101
102 if (count > 0)
103 {
104 count = MIN(count, BEEP_BUF_COUNT);
105 beep_count -= count;
106 *start = (unsigned char *)beep_buf;
107 *size = count * sizeof(uint32_t);
108 beep_generate(count);
109 }
110}
111
112/* Generates a constant square wave sound with a given frequency in Hertz for
113 a duration in milliseconds */
114void beep_play(unsigned int frequency, unsigned int duration,
115 unsigned int amplitude)
116{
117 mixer_channel_stop(PCM_MIXER_CHAN_BEEP);
118
119 if (frequency == 0 || duration == 0 || amplitude == 0)
120 return;
121
122 if (amplitude > INT16_MAX)
123 amplitude = INT16_MAX;
124
125 /* Setup the parameters for the square wave generator */
126 beep_phase = 0;
127 beep_step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
128 beep_count = NATIVE_FREQUENCY / 1000 * duration;
129 beep_amplitude = amplitude | (amplitude << 16); /* Word:|AMP16|AMP16| */
130
131 /* If it fits - avoid cb overhead */
132 unsigned char *start;
133 size_t size;
134
135 /* Generate first frame here */
136 beep_get_more(&start, &size);
137
138 mixer_channel_set_amplitude(PCM_MIXER_CHAN_BEEP, MIX_AMP_UNITY);
139 mixer_channel_play_data(PCM_MIXER_CHAN_BEEP,
140 beep_count ? beep_get_more : NULL,
141 start, size);
142}
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index e686fcc533..cbf003adbd 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -121,9 +121,11 @@ char* wps_default_skin(enum screen_type screen)
121 121
122void pause_action(bool may_fade, bool updatewps) 122void pause_action(bool may_fade, bool updatewps)
123{ 123{
124#if CONFIG_CODEC != SWCODEC
124 if (may_fade && global_settings.fade_on_stop) 125 if (may_fade && global_settings.fade_on_stop)
125 fade(false, updatewps); 126 fade(false, updatewps);
126 else 127 else
128#endif
127 audio_pause(); 129 audio_pause();
128 130
129 if (global_settings.pause_rewind) { 131 if (global_settings.pause_rewind) {
@@ -136,16 +138,22 @@ void pause_action(bool may_fade, bool updatewps)
136 - global_settings.pause_rewind * 1000; 138 - global_settings.pause_rewind * 1000;
137 audio_ff_rewind(newpos > 0 ? newpos : 0); 139 audio_ff_rewind(newpos > 0 ? newpos : 0);
138 } 140 }
141
142 (void)may_fade; (void)updatewps;
139} 143}
140 144
141void unpause_action(bool may_fade, bool updatewps) 145void unpause_action(bool may_fade, bool updatewps)
142{ 146{
147#if CONFIG_CODEC != SWCODEC
143 if (may_fade && global_settings.fade_on_stop) 148 if (may_fade && global_settings.fade_on_stop)
144 fade(true, updatewps); 149 fade(true, updatewps);
145 else 150 else
151#endif
146 audio_resume(); 152 audio_resume();
153 (void)may_fade; (void)updatewps;
147} 154}
148 155
156#if CONFIG_CODEC != SWCODEC
149void fade(bool fade_in, bool updatewps) 157void fade(bool fade_in, bool updatewps)
150{ 158{
151 int fp_global_vol = global_settings.volume << 8; 159 int fp_global_vol = global_settings.volume << 8;
@@ -204,6 +212,7 @@ void fade(bool fade_in, bool updatewps)
204 sound_set_volume(global_settings.volume); 212 sound_set_volume(global_settings.volume);
205 } 213 }
206} 214}
215#endif /* SWCODEC */
207 216
208static bool update_onvol_change(enum screen_type screen) 217static bool update_onvol_change(enum screen_type screen)
209{ 218{
@@ -569,7 +578,7 @@ static void play_hop(int direction)
569 { 578 {
570#if CONFIG_CODEC == SWCODEC 579#if CONFIG_CODEC == SWCODEC
571 if(global_settings.beep) 580 if(global_settings.beep)
572 pcmbuf_beep(1000, 150, 1500*global_settings.beep); 581 beep_play(1000, 150, 1500*global_settings.beep);
573#endif 582#endif
574 return; 583 return;
575 } 584 }
@@ -1127,9 +1136,12 @@ long gui_wps_show(void)
1127 status_set_record(false); 1136 status_set_record(false);
1128 status_set_audio(false); 1137 status_set_audio(false);
1129#endif 1138#endif
1139#if CONFIG_CODEC != SWCODEC
1130 if (global_settings.fade_on_stop) 1140 if (global_settings.fade_on_stop)
1131 fade(false, true); 1141 fade(false, true);
1132 1142#else
1143 audio_pause();
1144#endif
1133 if (bookmark) 1145 if (bookmark)
1134 bookmark_autobookmark(true); 1146 bookmark_autobookmark(true);
1135 audio_stop(); 1147 audio_stop();
diff --git a/apps/misc.c b/apps/misc.c
index 1f945c5431..a0817d7e27 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -297,12 +297,13 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
297 splashf(0, "%s %s", str(LANG_WARNING_BATTERY_EMPTY), 297 splashf(0, "%s %s", str(LANG_WARNING_BATTERY_EMPTY),
298 str(LANG_SHUTTINGDOWN)); 298 str(LANG_SHUTTINGDOWN));
299 } 299 }
300 300#if CONFIG_CODEC != SWCODEC
301 if (global_settings.fade_on_stop 301 if (global_settings.fade_on_stop
302 && (audio_stat & AUDIO_STATUS_PLAY)) 302 && (audio_stat & AUDIO_STATUS_PLAY))
303 { 303 {
304 fade(false, false); 304 fade(false, false);
305 } 305 }
306#endif
306 307
307 if (batt_safe) /* do not save on critical battery */ 308 if (batt_safe) /* do not save on critical battery */
308 { 309 {
@@ -380,8 +381,10 @@ bool list_stop_handler(void)
380 { 381 {
381 if (!global_settings.party_mode) 382 if (!global_settings.party_mode)
382 { 383 {
384#if CONFIG_CODEC != SWCODEC
383 if (global_settings.fade_on_stop) 385 if (global_settings.fade_on_stop)
384 fade(false, false); 386 fade(false, false);
387#endif
385 bookmark_autobookmark(true); 388 bookmark_autobookmark(true);
386 audio_stop(); 389 audio_stop();
387 ret = true; /* bookmarking can make a refresh necessary */ 390 ret = true; /* bookmarking can make a refresh necessary */
diff --git a/apps/misc.h b/apps/misc.h
index 0b155db1ec..c3c52d13e0 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -100,6 +100,9 @@ int clamp_value_wrap(int value, int max, int min);
100#endif 100#endif
101#endif 101#endif
102 102
103void beep_play(unsigned int frequency, unsigned int duration,
104 unsigned int amplitude);
105
103enum current_activity { 106enum current_activity {
104 ACTIVITY_UNKNOWN = 0, 107 ACTIVITY_UNKNOWN = 0,
105 ACTIVITY_MAINMENU, 108 ACTIVITY_MAINMENU,
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 7201d3981a..f2f94e3bc9 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -23,8 +23,9 @@
23#include "system.h" 23#include "system.h"
24#include "debug.h" 24#include "debug.h"
25#include <kernel.h> 25#include <kernel.h>
26#include "pcmbuf.h"
27#include "pcm.h" 26#include "pcm.h"
27#include "pcm_mixer.h"
28#include "pcmbuf.h"
28#include "playback.h" 29#include "playback.h"
29#include "codec_thread.h" 30#include "codec_thread.h"
30 31
@@ -49,9 +50,6 @@
49#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than 50#define PCMBUF_MIN_CHUNK 4096 /* We try to never feed a chunk smaller than
50 this to the DMA */ 51 this to the DMA */
51#define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */ 52#define CROSSFADE_BUFSIZE 8192 /* Size of the crossfade buffer */
52#define AUX_BUFSIZE 512 /* Size of the aux buffer; can be 512 if no
53 resampling or timestretching is allowed in
54 the aux channel, must be 2048 otherwise */
55 53
56/* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */ 54/* number of bytes played per second (sample rate * 2 channels * 2 bytes/sample) */
57#define BYTERATE (NATIVE_FREQUENCY * 4) 55#define BYTERATE (NATIVE_FREQUENCY * 4)
@@ -91,6 +89,12 @@ static struct chunkdesc *first_desc;
91/* Gapless playback */ 89/* Gapless playback */
92static bool track_transition IDATA_ATTR; 90static bool track_transition IDATA_ATTR;
93 91
92/* Fade effect */
93static unsigned int fade_vol = MIX_AMP_UNITY;
94
95/* Voice */
96static bool soft_mode = false;
97
94#ifdef HAVE_CROSSFADE 98#ifdef HAVE_CROSSFADE
95/* Crossfade buffer */ 99/* Crossfade buffer */
96static char *fadebuf IDATA_ATTR; 100static char *fadebuf IDATA_ATTR;
@@ -121,11 +125,6 @@ static size_t last_chunksize IDATA_ATTR;
121static size_t pcmbuf_unplayed_bytes IDATA_ATTR; 125static size_t pcmbuf_unplayed_bytes IDATA_ATTR;
122static size_t pcmbuf_watermark IDATA_ATTR; 126static size_t pcmbuf_watermark IDATA_ATTR;
123 127
124/* Voice */
125static char *voicebuf IDATA_ATTR;
126static struct chunkdesc *mix_chunk IDATA_ATTR;
127static size_t pcmbuf_mix_sample IDATA_ATTR;
128
129static bool low_latency_mode = false; 128static bool low_latency_mode = false;
130static bool flush_pcmbuf = false; 129static bool flush_pcmbuf = false;
131 130
@@ -317,10 +316,12 @@ static void boost_codec_thread(int pcm_fill_state)
317 * Also maintain buffer level above the watermark. */ 316 * Also maintain buffer level above the watermark. */
318static bool prepare_insert(size_t length) 317static bool prepare_insert(size_t length)
319{ 318{
319 bool playing = mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED;
320
320 if (low_latency_mode) 321 if (low_latency_mode)
321 { 322 {
322 /* 1/4s latency. */ 323 /* 1/4s latency. */
323 if (!LOW_DATA(1) && pcm_is_playing()) 324 if (!LOW_DATA(1) && playing)
324 return false; 325 return false;
325 } 326 }
326 327
@@ -329,7 +330,7 @@ static bool prepare_insert(size_t length)
329 return false; 330 return false;
330 331
331 /* Maintain the buffer level above the watermark */ 332 /* Maintain the buffer level above the watermark */
332 if (pcm_is_playing()) 333 if (playing)
333 { 334 {
334 /* Only codec thread initiates boost - voice boosts the cpu when playing 335 /* Only codec thread initiates boost - voice boosts the cpu when playing
335 a clip */ 336 a clip */
@@ -351,7 +352,7 @@ static bool prepare_insert(size_t length)
351 } 352 }
352#endif 353#endif
353 } 354 }
354 else /* pcm_is_playing */ 355 else /* !playing */
355 { 356 {
356 /* Boost CPU for pre-buffer */ 357 /* Boost CPU for pre-buffer */
357 trigger_cpu_boost(); 358 trigger_cpu_boost();
@@ -469,11 +470,14 @@ static size_t get_next_required_pcmbuf_size(void)
469 * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */ 470 * ...|---------PCMBUF---------|FADEBUF|VOICEBUF|DESCS|... */
470size_t pcmbuf_init(unsigned char *bufend) 471size_t pcmbuf_init(unsigned char *bufend)
471{ 472{
473 unsigned char *voicebuf;
474
472 pcmbuf_bufend = bufend; 475 pcmbuf_bufend = bufend;
473 pcmbuf_size = get_next_required_pcmbuf_size(); 476 pcmbuf_size = get_next_required_pcmbuf_size();
474 write_chunk = (struct chunkdesc *)pcmbuf_bufend - 477 write_chunk = (struct chunkdesc *)pcmbuf_bufend -
475 NUM_CHUNK_DESCS(pcmbuf_size); 478 NUM_CHUNK_DESCS(pcmbuf_size);
476 voicebuf = (char *)write_chunk - AUX_BUFSIZE; 479 voicebuf = (unsigned char *)write_chunk -
480 voicebuf_init((unsigned char *)write_chunk);
477#ifdef HAVE_CROSSFADE 481#ifdef HAVE_CROSSFADE
478 fadebuf = voicebuf - CROSSFADE_BUFSIZE; 482 fadebuf = voicebuf - CROSSFADE_BUFSIZE;
479 pcmbuffer = fadebuf - pcmbuf_size; 483 pcmbuffer = fadebuf - pcmbuf_size;
@@ -491,6 +495,8 @@ size_t pcmbuf_init(unsigned char *bufend)
491 495
492 pcmbuf_play_stop(); 496 pcmbuf_play_stop();
493 497
498 pcmbuf_soft_mode(false);
499
494 return pcmbuf_bufend - pcmbuffer; 500 return pcmbuf_bufend - pcmbuffer;
495} 501}
496 502
@@ -572,7 +578,7 @@ bool pcmbuf_start_track_change(bool auto_skip)
572#ifdef HAVE_CROSSFADE 578#ifdef HAVE_CROSSFADE
573 pcmbuf_is_crossfade_active() || 579 pcmbuf_is_crossfade_active() ||
574#endif 580#endif
575 !pcm_is_playing()) 581 mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED)
576 { 582 {
577 pcmbuf_play_stop(); 583 pcmbuf_play_stop();
578 pcm_play_unlock(); 584 pcm_play_unlock();
@@ -652,10 +658,6 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
652 write_end_chunk->link = pcmbuf_current; 658 write_end_chunk->link = pcmbuf_current;
653 write_end_chunk = pcmbuf_current; 659 write_end_chunk = pcmbuf_current;
654 660
655 /* If we've read over the mix chunk while it's still mixing there */
656 if (pcmbuf_current == mix_chunk)
657 mix_chunk = NULL;
658
659#ifdef HAVE_CROSSFADE 661#ifdef HAVE_CROSSFADE
660 /* If we've read over the crossfade chunk while it's still fading */ 662 /* If we've read over the crossfade chunk while it's still fading */
661 if (pcmbuf_current == crossfade_chunk) 663 if (pcmbuf_current == crossfade_chunk)
@@ -696,23 +698,23 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size)
696/* Force playback */ 698/* Force playback */
697void pcmbuf_play_start(void) 699void pcmbuf_play_start(void)
698{ 700{
699 if (!pcm_is_playing() && pcmbuf_unplayed_bytes && read_chunk != NULL) 701 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_STOPPED &&
702 pcmbuf_unplayed_bytes && read_chunk != NULL)
700 { 703 {
701 logf("pcmbuf_play_start"); 704 logf("pcmbuf_play_start");
702 last_chunksize = read_chunk->size; 705 last_chunksize = read_chunk->size;
703 pcmbuf_unplayed_bytes -= last_chunksize; 706 pcmbuf_unplayed_bytes -= last_chunksize;
704 pcm_play_data(pcmbuf_pcm_callback, 707 mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK,
705 read_chunk->addr, last_chunksize); 708 pcmbuf_pcm_callback, NULL, 0);
706 } 709 }
707} 710}
708 711
709void pcmbuf_play_stop(void) 712void pcmbuf_play_stop(void)
710{ 713{
711 logf("pcmbuf_play_stop"); 714 logf("pcmbuf_play_stop");
712 pcm_play_stop(); 715 mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
713 716
714 pcmbuf_unplayed_bytes = 0; 717 pcmbuf_unplayed_bytes = 0;
715 mix_chunk = NULL;
716 if (read_chunk) { 718 if (read_chunk) {
717 write_end_chunk->link = read_chunk; 719 write_end_chunk->link = read_chunk;
718 write_end_chunk = read_end_chunk; 720 write_end_chunk = read_end_chunk;
@@ -737,8 +739,9 @@ void pcmbuf_play_stop(void)
737void pcmbuf_pause(bool pause) 739void pcmbuf_pause(bool pause)
738{ 740{
739 logf("pcmbuf_pause: %s", pause?"pause":"play"); 741 logf("pcmbuf_pause: %s", pause?"pause":"play");
740 if (pcm_is_playing()) 742
741 pcm_play_pause(!pause); 743 if (mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_STOPPED)
744 mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, !pause);
742 else if (!pause) 745 else if (!pause)
743 pcmbuf_play_start(); 746 pcmbuf_play_start();
744} 747}
@@ -1031,102 +1034,6 @@ bool pcmbuf_is_same_size(void)
1031#endif /* HAVE_CROSSFADE */ 1034#endif /* HAVE_CROSSFADE */
1032 1035
1033 1036
1034/** Voice */
1035
1036/* Returns pcm buffer usage in percents (0 to 100). */
1037static int pcmbuf_usage(void)
1038{
1039 return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
1040}
1041
1042static int pcmbuf_mix_free(void)
1043{
1044 if (mix_chunk)
1045 {
1046 size_t my_mix_end =
1047 (size_t)&((int16_t *)mix_chunk->addr)[pcmbuf_mix_sample];
1048 size_t my_write_pos = (size_t)&pcmbuffer[pcmbuffer_pos];
1049 if (my_write_pos < my_mix_end)
1050 my_write_pos += pcmbuf_size;
1051 return (my_write_pos - my_mix_end) * 100 / pcmbuf_unplayed_bytes;
1052 }
1053 return 100;
1054}
1055
1056void *pcmbuf_request_voice_buffer(int *count)
1057{
1058 /* A get-it-to-work-for-now hack (audio status could change by
1059 completion) */
1060 if (audio_status() & AUDIO_STATUS_PLAY)
1061 {
1062 if (read_chunk == NULL)
1063 {
1064 return NULL;
1065 }
1066 else if (pcmbuf_usage() >= 10 && pcmbuf_mix_free() >= 30 &&
1067 (mix_chunk || read_chunk->link))
1068 {
1069 *count = MIN(*count, AUX_BUFSIZE/4);
1070 return voicebuf;
1071 }
1072 else
1073 {
1074 return NULL;
1075 }
1076 }
1077 else
1078 {
1079 return pcmbuf_request_buffer(count);
1080 }
1081}
1082
1083void pcmbuf_write_voice_complete(int count)
1084{
1085 /* A get-it-to-work-for-now hack (audio status could have changed) */
1086 if (!(audio_status() & AUDIO_STATUS_PLAY))
1087 {
1088 pcmbuf_write_complete(count);
1089 return;
1090 }
1091
1092 int16_t *ibuf = (int16_t *)voicebuf;
1093 int16_t *obuf;
1094 size_t chunk_samples;
1095
1096 if (mix_chunk == NULL && read_chunk != NULL)
1097 {
1098 mix_chunk = read_chunk->link;
1099 /* Start 1/8s into the next chunk */
1100 pcmbuf_mix_sample = BYTERATE / 16;
1101 }
1102
1103 if (!mix_chunk)
1104 return;
1105
1106 obuf = (int16_t *)mix_chunk->addr;
1107 chunk_samples = mix_chunk->size / sizeof (int16_t);
1108
1109 count <<= 1;
1110
1111 while (count-- > 0)
1112 {
1113 int32_t sample = *ibuf++;
1114
1115 if (pcmbuf_mix_sample >= chunk_samples)
1116 {
1117 mix_chunk = mix_chunk->link;
1118 if (!mix_chunk)
1119 return;
1120 pcmbuf_mix_sample = 0;
1121 obuf = (int16_t *)mix_chunk->addr;
1122 chunk_samples = mix_chunk->size / 2;
1123 }
1124 sample += obuf[pcmbuf_mix_sample] >> 2;
1125 obuf[pcmbuf_mix_sample++] = clip_sample_16(sample);
1126 }
1127}
1128
1129
1130/** Debug menu, other metrics */ 1037/** Debug menu, other metrics */
1131 1038
1132/* Amount of bytes left in the buffer. */ 1039/* Amount of bytes left in the buffer. */
@@ -1174,6 +1081,71 @@ unsigned char *pcmbuf_get_meminfo(size_t *length)
1174#endif 1081#endif
1175 1082
1176 1083
1084/** Fading and channel volume control */
1085
1086/* Sync the channel amplitude to all states */
1087static void pcmbuf_update_volume(void)
1088{
1089 unsigned int vol = fade_vol;
1090
1091 if (soft_mode)
1092 vol >>= 2;
1093
1094 mixer_channel_set_amplitude(PCM_MIXER_CHAN_PLAYBACK, vol);
1095}
1096
1097/* Quiet-down the channel if 'shhh' is true or else play at normal level */
1098void pcmbuf_soft_mode(bool shhh)
1099{
1100 soft_mode = shhh;
1101 pcmbuf_update_volume();
1102}
1103
1104/* Fade channel in or out */
1105void pcmbuf_fade(bool fade, bool in)
1106{
1107 if (!fade)
1108 {
1109 /* Simply set the level */
1110 fade_vol = in ? MIX_AMP_UNITY : MIX_AMP_MUTE;
1111 }
1112 else
1113 {
1114 /* Start from the opposing end */
1115 fade_vol = in ? MIX_AMP_MUTE : MIX_AMP_UNITY;
1116 }
1117
1118 pcmbuf_update_volume();
1119
1120 if (fade)
1121 {
1122 /* Do this on thread for now */
1123 int old_prio = thread_set_priority(thread_self(), PRIORITY_REALTIME);
1124
1125 while (1)
1126 {
1127 /* Linear fade actually sounds better */
1128 if (in)
1129 fade_vol += MIN(MIX_AMP_UNITY/16, MIX_AMP_UNITY - fade_vol);
1130 else
1131 fade_vol -= MIN(MIX_AMP_UNITY/16, fade_vol - MIX_AMP_MUTE);
1132
1133 pcmbuf_update_volume();
1134
1135 if (fade_vol > MIX_AMP_MUTE && fade_vol < MIX_AMP_UNITY)
1136 {
1137 sleep(0);
1138 continue;
1139 }
1140
1141 break;
1142 }
1143
1144 thread_set_priority(thread_self(), old_prio);
1145 }
1146}
1147
1148
1177/** Misc */ 1149/** Misc */
1178 1150
1179bool pcmbuf_is_lowdata(void) 1151bool pcmbuf_is_lowdata(void)
@@ -1201,107 +1173,6 @@ void pcmbuf_set_low_latency(bool state)
1201 1173
1202unsigned long pcmbuf_get_latency(void) 1174unsigned long pcmbuf_get_latency(void)
1203{ 1175{
1204 return (pcmbuf_unplayed_bytes + pcm_get_bytes_waiting()) * 1000 / BYTERATE; 1176 return (pcmbuf_unplayed_bytes +
1205} 1177 mixer_channel_get_bytes_waiting(PCM_MIXER_CHAN_PLAYBACK)) * 1000 / BYTERATE;
1206
1207#ifndef HAVE_HARDWARE_BEEP
1208#define MINIBUF_SAMPLES (NATIVE_FREQUENCY / 1000 * KEYCLICK_DURATION)
1209#define MINIBUF_SIZE (MINIBUF_SAMPLES*4)
1210
1211/* Generates a constant square wave sound with a given frequency
1212 in Hertz for a duration in milliseconds. */
1213void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
1214{
1215 unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency;
1216 int32_t phase = 0;
1217 int16_t *bufptr, *bufstart, *bufend;
1218 int32_t sample;
1219 int nsamples = NATIVE_FREQUENCY / 1000 * duration;
1220 bool mix = read_chunk != NULL && read_chunk->link != NULL;
1221 int i;
1222
1223 bufend = SKIPBYTES((int16_t *)pcmbuffer, pcmbuf_size);
1224
1225 /* Find the insertion point and set bufstart to the start of it */
1226 if (mix)
1227 {
1228 /* Get the currently playing chunk at the current position. */
1229 bufstart = (int16_t *)pcm_play_dma_get_peak_buffer(&i);
1230
1231 /* If above isn't implemented or pcm is stopped, no beepeth. */
1232 if (!bufstart || !pcm_is_playing())
1233 return;
1234
1235 /* Give 5ms clearance. */
1236 bufstart += BYTERATE / 200;
1237
1238#ifdef HAVE_PCM_DMA_ADDRESS
1239 /* Returned peak addresses are DMA addresses */
1240 bufend = pcm_dma_addr(bufend);
1241#endif
1242
1243 /* Wrapped above? */
1244 if (bufstart >= bufend)
1245 bufstart -= pcmbuf_size;
1246
1247 /* NOTE: On some targets using hardware DMA, cache range flushing may
1248 * be required or the writes may not be picked up by the controller.
1249 * An incremental flush should be done periodically during the mixdown. */
1250 }
1251 else if (nsamples <= MINIBUF_SAMPLES)
1252 {
1253 static int16_t minibuf[MINIBUF_SAMPLES*2] __attribute__((aligned(4)));
1254 /* Use mini buffer */
1255 bufstart = minibuf;
1256 bufend = SKIPBYTES(bufstart, MINIBUF_SIZE);
1257 }
1258 else if (!audio_buffer_state_trashed())
1259 {
1260 /* Use pcmbuffer */
1261 bufstart = (int16_t *)pcmbuffer;
1262 }
1263 else
1264 {
1265 /* No place */
1266 return;
1267 }
1268
1269 bufptr = bufstart;
1270
1271 /* Mix square wave into buffer */
1272 for (i = 0; i < nsamples; ++i)
1273 {
1274 int32_t amp = (phase >> 31) ^ (int32_t)amplitude;
1275 sample = mix ? *bufptr : 0;
1276 *bufptr++ = clip_sample_16(sample + amp);
1277 if (bufptr >= bufend)
1278 bufptr = (int16_t *)pcmbuffer;
1279 sample = mix ? *bufptr : 0;
1280 *bufptr++ = clip_sample_16(sample + amp);
1281 if (bufptr >= bufend)
1282 bufptr = (int16_t *)pcmbuffer;
1283
1284 phase += step;
1285 }
1286
1287 pcm_play_lock();
1288#ifdef HAVE_RECORDING
1289 pcm_rec_lock();
1290#endif
1291
1292 /* Kick off playback if required and it won't interfere */
1293 if (!pcm_is_playing()
1294#ifdef HAVE_RECORDING
1295 && !pcm_is_recording()
1296#endif
1297 )
1298 {
1299 pcm_play_data(NULL, (unsigned char *)bufstart, nsamples * 4);
1300 }
1301
1302 pcm_play_unlock();
1303#ifdef HAVE_RECORDING
1304 pcm_rec_unlock();
1305#endif
1306} 1178}
1307#endif /* HAVE_HARDWARE_BEEP */
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index b7bf8c2b16..b7f5a3c2d6 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -64,9 +64,10 @@ unsigned char *pcmbuf_get_meminfo(size_t *length);
64#endif 64#endif
65 65
66/* Misc */ 66/* Misc */
67void pcmbuf_fade(bool fade, bool in);
68void pcmbuf_soft_mode(bool shhh);
67bool pcmbuf_is_lowdata(void); 69bool pcmbuf_is_lowdata(void);
68void pcmbuf_set_low_latency(bool state); 70void pcmbuf_set_low_latency(bool state);
69unsigned long pcmbuf_get_latency(void); 71unsigned long pcmbuf_get_latency(void);
70void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude);
71 72
72#endif 73#endif
diff --git a/apps/playback.c b/apps/playback.c
index 2775e8a95b..cbb94a9d22 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -39,6 +39,7 @@
39#include "abrepeat.h" 39#include "abrepeat.h"
40#include "pcmbuf.h" 40#include "pcmbuf.h"
41#include "playback.h" 41#include "playback.h"
42#include "misc.h"
42 43
43#ifdef HAVE_TAGCACHE 44#ifdef HAVE_TAGCACHE
44#include "tagcache.h" 45#include "tagcache.h"
@@ -2360,6 +2361,9 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2360#ifndef PLATFORM_HAS_VOLUME_CHANGE 2361#ifndef PLATFORM_HAS_VOLUME_CHANGE
2361 sound_set_volume(global_settings.volume); 2362 sound_set_volume(global_settings.volume);
2362#endif 2363#endif
2364 /* Be sure channel is audible */
2365 pcmbuf_fade(false, true);
2366
2363 /* Update our state */ 2367 /* Update our state */
2364 play_status = PLAY_PLAYING; 2368 play_status = PLAY_PLAYING;
2365 } 2369 }
@@ -2413,6 +2417,8 @@ static void audio_stop_playback(void)
2413 if (play_status == PLAY_STOPPED) 2417 if (play_status == PLAY_STOPPED)
2414 return; 2418 return;
2415 2419
2420 pcmbuf_fade(global_settings.fade_on_stop, false);
2421
2416 /* Stop the codec and unload it */ 2422 /* Stop the codec and unload it */
2417 halt_decoding_track(true); 2423 halt_decoding_track(true);
2418 pcmbuf_play_stop(); 2424 pcmbuf_play_stop();
@@ -2452,6 +2458,11 @@ static void audio_on_pause(bool pause)
2452 if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED)) 2458 if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED))
2453 return; 2459 return;
2454 2460
2461 bool const do_fade = global_settings.fade_on_stop;
2462
2463 if (pause)
2464 pcmbuf_fade(do_fade, false);
2465
2455 if (!ff_rw_mode) 2466 if (!ff_rw_mode)
2456 { 2467 {
2457 /* Not in ff/rw mode - may set the state (otherwise this could make 2468 /* Not in ff/rw mode - may set the state (otherwise this could make
@@ -2459,6 +2470,9 @@ static void audio_on_pause(bool pause)
2459 pcmbuf_pause(pause); 2470 pcmbuf_pause(pause);
2460 } 2471 }
2461 2472
2473 if (!pause)
2474 pcmbuf_fade(do_fade, true);
2475
2462 play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; 2476 play_status = pause ? PLAY_PAUSED : PLAY_PLAYING;
2463 2477
2464 if (!pause && codec_skip_pending) 2478 if (!pause && codec_skip_pending)
@@ -3170,7 +3184,7 @@ void audio_pcmbuf_track_change(bool pcmbuf)
3170/* May pcmbuf start PCM playback when the buffer is full enough? */ 3184/* May pcmbuf start PCM playback when the buffer is full enough? */
3171bool audio_pcmbuf_may_play(void) 3185bool audio_pcmbuf_may_play(void)
3172{ 3186{
3173 return play_status != PLAY_PAUSED && !ff_rw_mode; 3187 return play_status == PLAY_PLAYING && !ff_rw_mode;
3174} 3188}
3175 3189
3176 3190
@@ -3339,7 +3353,7 @@ void audio_skip(int offset)
3339 skip_offset = accum; 3353 skip_offset = accum;
3340 3354
3341 if (global_settings.beep) 3355 if (global_settings.beep)
3342 pcmbuf_beep(2000, 100, 2500*global_settings.beep); 3356 beep_play(2000, 100, 2500*global_settings.beep);
3343 3357
3344 LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", offset); 3358 LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", offset);
3345 3359
@@ -3360,7 +3374,7 @@ void audio_skip(int offset)
3360 { 3374 {
3361 /* No more tracks */ 3375 /* No more tracks */
3362 if (global_settings.beep) 3376 if (global_settings.beep)
3363 pcmbuf_beep(1000, 100, 1500*global_settings.beep); 3377 beep_play(1000, 100, 1500*global_settings.beep);
3364 } 3378 }
3365 3379
3366 id3_mutex_unlock(); 3380 id3_mutex_unlock();
diff --git a/apps/plugin.c b/apps/plugin.c
index d9f7c4e24c..10cb9263a4 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -551,7 +551,7 @@ static const struct plugin_api rockbox_api = {
551 pcm_get_peak_buffer, 551 pcm_get_peak_buffer,
552 pcm_play_lock, 552 pcm_play_lock,
553 pcm_play_unlock, 553 pcm_play_unlock,
554 pcmbuf_beep, 554 beep_play,
555#ifdef HAVE_RECORDING 555#ifdef HAVE_RECORDING
556 &rec_freq_sampr[0], 556 &rec_freq_sampr[0],
557 pcm_init_recording, 557 pcm_init_recording,
@@ -778,6 +778,9 @@ static const struct plugin_api rockbox_api = {
778 778
779 /* new stuff at the end, sort into place next time 779 /* new stuff at the end, sort into place next time
780 the API gets incompatible */ 780 the API gets incompatible */
781
782 mixer_channel_status,
783 mixer_channel_get_buffer,
781}; 784};
782 785
783int plugin_load(const char* plugin, const void* parameter) 786int plugin_load(const char* plugin, const void* parameter)
diff --git a/apps/plugin.h b/apps/plugin.h
index f15c626667..113296c19a 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -65,6 +65,7 @@ void* plugin_get_buffer(size_t *buffer_size);
65#include "misc.h" 65#include "misc.h"
66#include "filefuncs.h" 66#include "filefuncs.h"
67#if (CONFIG_CODEC == SWCODEC) 67#if (CONFIG_CODEC == SWCODEC)
68#include "pcm_mixer.h"
68#include "dsp.h" 69#include "dsp.h"
69#include "codecs.h" 70#include "codecs.h"
70#include "playback.h" 71#include "playback.h"
@@ -145,7 +146,7 @@ void* plugin_get_buffer(size_t *buffer_size);
145#define PLUGIN_MAGIC 0x526F634B /* RocK */ 146#define PLUGIN_MAGIC 0x526F634B /* RocK */
146 147
147/* increase this every time the api struct changes */ 148/* increase this every time the api struct changes */
148#define PLUGIN_API_VERSION 205 149#define PLUGIN_API_VERSION 206
149 150
150/* update this to latest version if a change to the api struct breaks 151/* update this to latest version if a change to the api struct breaks
151 backwards compatibility (and please take the opportunity to sort in any 152 backwards compatibility (and please take the opportunity to sort in any
@@ -635,9 +636,8 @@ struct plugin_api {
635 const void* (*pcm_get_peak_buffer)(int *count); 636 const void* (*pcm_get_peak_buffer)(int *count);
636 void (*pcm_play_lock)(void); 637 void (*pcm_play_lock)(void);
637 void (*pcm_play_unlock)(void); 638 void (*pcm_play_unlock)(void);
638 void (*pcmbuf_beep)(unsigned int frequency, 639 void (*beep_play)(unsigned int frequency, unsigned int duration,
639 size_t duration, 640 unsigned int amplitude);
640 int amplitude);
641#ifdef HAVE_RECORDING 641#ifdef HAVE_RECORDING
642 const unsigned long *rec_freq_sampr; 642 const unsigned long *rec_freq_sampr;
643 void (*pcm_init_recording)(void); 643 void (*pcm_init_recording)(void);
@@ -908,6 +908,8 @@ struct plugin_api {
908 908
909 /* new stuff at the end, sort into place next time 909 /* new stuff at the end, sort into place next time
910 the API gets incompatible */ 910 the API gets incompatible */
911 enum channel_status (*mixer_channel_status)(enum pcm_mixer_channel channel);
912 void * (*mixer_channel_get_buffer)(enum pcm_mixer_channel channel, int *count);
911}; 913};
912 914
913/* plugin header */ 915/* plugin header */
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index b6b1e2fead..a920f8c7f1 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -1137,6 +1137,10 @@ static void draw_spectrogram_horizontal(void)
1137/********************* End of plotting functions (modes) *********************/ 1137/********************* End of plotting functions (modes) *********************/
1138 1138
1139/****************************** FFT functions ********************************/ 1139/****************************** FFT functions ********************************/
1140static bool is_playing(void)
1141{
1142 return rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) == CHANNEL_PLAYING;
1143}
1140 1144
1141/** functions use in single/multi configuration **/ 1145/** functions use in single/multi configuration **/
1142static inline bool fft_init_fft_lib(void) 1146static inline bool fft_init_fft_lib(void)
@@ -1156,7 +1160,8 @@ static inline bool fft_init_fft_lib(void)
1156static inline bool fft_get_fft(void) 1160static inline bool fft_get_fft(void)
1157{ 1161{
1158 int count; 1162 int count;
1159 int16_t *value = (int16_t *) rb->pcm_get_peak_buffer(&count); 1163 int16_t *value =
1164 (int16_t *) rb->mixer_channel_get_buffer(PCM_MIXER_CHAN_PLAYBACK, &count);
1160 /* This block can introduce discontinuities in our data. Meaning, the 1165 /* This block can introduce discontinuities in our data. Meaning, the
1161 * FFT will not be done a continuous segment of the signal. Which can 1166 * FFT will not be done a continuous segment of the signal. Which can
1162 * be bad. Or not. 1167 * be bad. Or not.
@@ -1214,7 +1219,7 @@ static void fft_thread_entry(void)
1214 1219
1215 while(fft_thread_run) 1220 while(fft_thread_run)
1216 { 1221 {
1217 if (!rb->pcm_is_playing()) 1222 if (!is_playing())
1218 { 1223 {
1219 rb->sleep(HZ/5); 1224 rb->sleep(HZ/5);
1220 continue; 1225 continue;
@@ -1296,7 +1301,7 @@ static void fft_close_fft(void)
1296 * target uses IRAM */ 1301 * target uses IRAM */
1297static bool fft_have_fft(void) 1302static bool fft_have_fft(void)
1298{ 1303{
1299 return rb->pcm_is_playing() && fft_get_fft(); 1304 return is_playing() && fft_get_fft();
1300} 1305}
1301 1306
1302static inline void fft_free_fft_output(void) 1307static inline void fft_free_fft_output(void)
@@ -1366,7 +1371,7 @@ enum plugin_status plugin_start(const void* parameter)
1366 { 1371 {
1367 int timeout; 1372 int timeout;
1368 1373
1369 if(!rb->pcm_is_playing()) 1374 if(!is_playing())
1370 { 1375 {
1371 showing_warning = true; 1376 showing_warning = true;
1372 mylcd_clear_display(); 1377 mylcd_clear_display();
diff --git a/apps/recorder/keyboard.c b/apps/recorder/keyboard.c
index 1b2d76eab2..5f2a32c367 100644
--- a/apps/recorder/keyboard.c
+++ b/apps/recorder/keyboard.c
@@ -1231,7 +1231,7 @@ static void kbd_move_cursor(struct edit_state *state, int dir)
1231 state->editpos -= dir; 1231 state->editpos -= dir;
1232#if CONFIG_CODEC == SWCODEC 1232#if CONFIG_CODEC == SWCODEC
1233 if (global_settings.talk_menu) 1233 if (global_settings.talk_menu)
1234 pcmbuf_beep(1000, 150, 1500); 1234 beep_play(1000, 150, 1500);
1235#endif 1235#endif
1236 } 1236 }
1237} 1237}
diff --git a/apps/voice_thread.c b/apps/voice_thread.c
index 6683fcc067..3318bbecb3 100644
--- a/apps/voice_thread.c
+++ b/apps/voice_thread.c
@@ -27,6 +27,8 @@
27#include "audio.h" 27#include "audio.h"
28#include "playback.h" 28#include "playback.h"
29#include "pcmbuf.h" 29#include "pcmbuf.h"
30#include "pcm.h"
31#include "pcm_mixer.h"
30#include "codecs/libspeex/speex/speex.h" 32#include "codecs/libspeex/speex/speex.h"
31 33
32/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log 34/* Define any of these as "1" and uncomment the LOGF_ENABLE line to log
@@ -53,24 +55,50 @@
53#define IBSS_ATTR_VOICE_STACK IBSS_ATTR 55#define IBSS_ATTR_VOICE_STACK IBSS_ATTR
54#endif 56#endif
55 57
58/* Minimum priority needs to be a bit elevated since voice has fairly low
59 latency */
60#define PRIORITY_VOICE (PRIORITY_PLAYBACK-4)
61
56#define VOICE_FRAME_SIZE 320 /* Samples / frame */ 62#define VOICE_FRAME_SIZE 320 /* Samples / frame */
57#define VOICE_SAMPLE_RATE 16000 /* Sample rate in HZ */ 63#define VOICE_SAMPLE_RATE 16000 /* Sample rate in HZ */
58#define VOICE_SAMPLE_DEPTH 16 /* Sample depth in bits */ 64#define VOICE_SAMPLE_DEPTH 16 /* Sample depth in bits */
59 65
60/* Voice thread variables */ 66/* Voice thread variables */
61static unsigned int voice_thread_id = 0; 67static unsigned int voice_thread_id = 0;
62static long voice_stack[(DEFAULT_STACK_SIZE + 0x3C0)/sizeof(long)] IBSS_ATTR_VOICE_STACK; 68#ifdef CPU_COLDFIRE
69/* ISR uses any available stack - need a bit more room */
70#define VOICE_STACK_EXTRA 0x400
71#else
72#define VOICE_STACK_EXTRA 0x3c0
73#endif
74static long voice_stack[(DEFAULT_STACK_SIZE + VOICE_STACK_EXTRA)/sizeof(long)]
75 IBSS_ATTR_VOICE_STACK;
63static const char voice_thread_name[] = "voice"; 76static const char voice_thread_name[] = "voice";
64 77
65/* Voice thread synchronization objects */ 78/* Voice thread synchronization objects */
66static struct event_queue voice_queue SHAREDBSS_ATTR; 79static struct event_queue voice_queue SHAREDBSS_ATTR;
67static struct mutex voice_mutex SHAREDBSS_ATTR;
68static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR; 80static struct queue_sender_list voice_queue_sender_list SHAREDBSS_ATTR;
69static bool voice_done SHAREDDATA_ATTR = true; 81static bool voice_done SHAREDDATA_ATTR = true;
70 82
71/* Buffer for decoded samples */ 83/* Buffer for decoded samples */
72static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR; 84static spx_int16_t voice_output_buf[VOICE_FRAME_SIZE] CACHEALIGN_ATTR;
73 85
86#define VOICE_PCM_FRAME_COUNT ((NATIVE_FREQUENCY*VOICE_FRAME_SIZE + \
87 VOICE_SAMPLE_RATE) / VOICE_SAMPLE_RATE)
88#define VOICE_PCM_FRAME_SIZE (VOICE_PCM_FRAME_COUNT*4)
89
90/* Default number of native-frequency PCM frames to queue - adjust as
91 necessary per-target */
92#define VOICE_FRAMES 3
93
94/* Might have lookahead and be skipping samples, so size is needed */
95static size_t voicebuf_sizes[VOICE_FRAMES];
96static uint32_t (* voicebuf)[VOICE_PCM_FRAME_COUNT];
97static unsigned int cur_buf_in, cur_buf_out;
98
99/* A delay to not bring audio back to normal level too soon */
100#define QUIET_COUNT 3
101
74enum voice_thread_states 102enum voice_thread_states
75{ 103{
76 TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */ 104 TSTATE_STOPPED = 0, /* Voice thread is stopped and awaiting commands */
@@ -83,7 +111,6 @@ enum voice_thread_messages
83 Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */ 111 Q_VOICE_NULL = 0, /* A message for thread sync - no effect on state */
84 Q_VOICE_PLAY, /* Play a clip */ 112 Q_VOICE_PLAY, /* Play a clip */
85 Q_VOICE_STOP, /* Stop current clip */ 113 Q_VOICE_STOP, /* Stop current clip */
86 Q_VOICE_STATE, /* Query playing state */
87}; 114};
88 115
89/* Structure to store clip data callback info */ 116/* Structure to store clip data callback info */
@@ -98,7 +125,7 @@ struct voice_info
98 * internal functions */ 125 * internal functions */
99struct voice_thread_data 126struct voice_thread_data
100{ 127{
101 int state; /* Thread state (TSTATE_*) */ 128 volatile int state; /* Thread state (TSTATE_*) */
102 struct queue_event ev; /* Last queue event pulled from queue */ 129 struct queue_event ev; /* Last queue event pulled from queue */
103 void *st; /* Decoder instance */ 130 void *st; /* Decoder instance */
104 SpeexBits bits; /* Bit cursor */ 131 SpeexBits bits; /* Bit cursor */
@@ -107,33 +134,79 @@ struct voice_thread_data
107 const char *src[2]; /* Current output buffer pointers */ 134 const char *src[2]; /* Current output buffer pointers */
108 int lookahead; /* Number of samples to drop at start of clip */ 135 int lookahead; /* Number of samples to drop at start of clip */
109 int count; /* Count of samples remaining to send to PCM */ 136 int count; /* Count of samples remaining to send to PCM */
137 int quiet_counter; /* Countdown until audio goes back to normal */
110}; 138};
111 139
112/* Audio playback is in a playing state? */ 140/* Number of frames in queue */
113static inline bool playback_is_playing(void) 141static inline int voice_unplayed_frames(void)
114{ 142{
115 return (audio_status() & AUDIO_STATUS_PLAY) != 0; 143 return cur_buf_in - cur_buf_out;
144}
145
146/* Mixer channel callback */
147static void voice_pcm_callback(unsigned char **start, size_t *size)
148{
149 if (voice_unplayed_frames() == 0)
150 return; /* Done! */
151
152 unsigned int i = ++cur_buf_out % VOICE_FRAMES;
153
154 *start = (unsigned char *)voicebuf[i];
155 *size = voicebuf_sizes[i];
156}
157
158/* Start playback of voice channel if not already playing */
159static void voice_start_playback(void)
160{
161 if (mixer_channel_status(PCM_MIXER_CHAN_VOICE) != CHANNEL_STOPPED)
162 return;
163
164 unsigned int i = cur_buf_out % VOICE_FRAMES;
165 mixer_channel_play_data(PCM_MIXER_CHAN_VOICE, voice_pcm_callback,
166 (unsigned char *)voicebuf[i], voicebuf_sizes[i]);
167}
168
169/* Stop the voice channel */
170static void voice_stop_playback(void)
171{
172 mixer_channel_stop(PCM_MIXER_CHAN_VOICE);
173 cur_buf_in = cur_buf_out = 0;
174}
175
176/* Grab a free PCM frame */
177static uint32_t * voice_buf_get(void)
178{
179 if (voice_unplayed_frames() >= VOICE_FRAMES)
180 {
181 /* Full */
182 voice_start_playback();
183 return NULL;
184 }
185
186 return voicebuf[cur_buf_in % VOICE_FRAMES];
187}
188
189/* Commit a frame returned by voice_buf_get and set the actual size */
190static void voice_buf_commit(size_t size)
191{
192 voicebuf_sizes[cur_buf_in++ % VOICE_FRAMES] = size;
116} 193}
117 194
118/* Stop any current clip and start playing a new one */ 195/* Stop any current clip and start playing a new one */
119void mp3_play_data(const unsigned char* start, int size, 196void mp3_play_data(const unsigned char* start, int size,
120 pcm_play_callback_type get_more) 197 pcm_play_callback_type get_more)
121{ 198{
122 /* Shared struct to get data to the thread - once it replies, it has
123 * safely cached it in its own private data */
124 static struct voice_info voice_clip SHAREDBSS_ATTR;
125
126 if (get_more != NULL && start != NULL && (ssize_t)size > 0) 199 if (get_more != NULL && start != NULL && (ssize_t)size > 0)
127 { 200 {
128 mutex_lock(&voice_mutex); 201 struct voice_info voice_clip =
202 {
203 .get_more = get_more,
204 .start = (unsigned char *)start,
205 .size = size,
206 };
129 207
130 voice_clip.get_more = get_more;
131 voice_clip.start = (unsigned char *)start;
132 voice_clip.size = size;
133 LOGFQUEUE("mp3 >| voice Q_VOICE_PLAY"); 208 LOGFQUEUE("mp3 >| voice Q_VOICE_PLAY");
134 queue_send(&voice_queue, Q_VOICE_PLAY, (intptr_t)&voice_clip); 209 queue_send(&voice_queue, Q_VOICE_PLAY, (intptr_t)&voice_clip);
135
136 mutex_unlock(&voice_mutex);
137 } 210 }
138} 211}
139 212
@@ -143,11 +216,8 @@ void mp3_play_stop(void)
143 if(!audio_is_thread_ready()) 216 if(!audio_is_thread_ready())
144 return; 217 return;
145 218
146 mutex_lock(&voice_mutex); /* Sync against voice_stop */ 219 LOGFQUEUE("mp3 >| voice Q_VOICE_STOP");
147 LOGFQUEUE("mp3 >| voice Q_VOICE_STOP: 1"); 220 queue_send(&voice_queue, Q_VOICE_STOP, 0);
148 queue_send(&voice_queue, Q_VOICE_STOP, 1);
149
150 mutex_unlock(&voice_mutex);
151} 221}
152 222
153void mp3_play_pause(bool play) 223void mp3_play_pause(bool play)
@@ -156,36 +226,19 @@ void mp3_play_pause(bool play)
156 (void)play; 226 (void)play;
157} 227}
158 228
159/* Tell is voice is still in a playing state */ 229/* Tell if voice is still in a playing state */
160bool mp3_is_playing(void) 230bool mp3_is_playing(void)
161{ 231{
162 /* TODO: Implement a timeout or state query function for event objects */ 232 return !voice_done;
163 LOGFQUEUE("mp3 >| voice Q_VOICE_STATE");
164 int state = queue_send(&voice_queue, Q_VOICE_STATE, 0);
165 return state != TSTATE_STOPPED;
166} 233}
167 234
168/* This function is meant to be used by the buffer request functions to 235/* This function is meant to be used by the buffer request functions to
169 ensure the codec is no longer active */ 236 ensure the codec is no longer active */
170void voice_stop(void) 237void voice_stop(void)
171{ 238{
172 mutex_lock(&voice_mutex);
173
174 /* Stop the output and current clip */
175 mp3_play_stop();
176
177 /* Careful if using sync objects in talk.c - make sure locking order is
178 * observed with one or the other always granted first */
179
180 /* Unqueue all future clips */ 239 /* Unqueue all future clips */
181 talk_force_shutup(); 240 talk_force_shutup();
182 241}
183 /* Wait for any final queue_post to be processed */
184 LOGFQUEUE("mp3 >| voice Q_VOICE_NULL");
185 queue_send(&voice_queue, Q_VOICE_NULL, 0);
186
187 mutex_unlock(&voice_mutex);
188} /* voice_stop */
189 242
190/* Wait for voice to finish speaking. */ 243/* Wait for voice to finish speaking. */
191void voice_wait(void) 244void voice_wait(void)
@@ -194,8 +247,7 @@ void voice_wait(void)
194 * new clip by the time we wait. This should be resolvable if conditions 247 * new clip by the time we wait. This should be resolvable if conditions
195 * ever require knowing the very clip you requested has finished. */ 248 * ever require knowing the very clip you requested has finished. */
196 249
197 /* Wait for PCM buffer to be exhausted. Works only if not playing. */ 250 while (!voice_done)
198 while(!voice_done || (!playback_is_playing() && pcm_is_playing()))
199 sleep(1); 251 sleep(1);
200} 252}
201 253
@@ -211,6 +263,9 @@ static void voice_data_init(struct voice_thread_data *td)
211 dsp_configure(td->dsp, DSP_SET_FREQUENCY, VOICE_SAMPLE_RATE); 263 dsp_configure(td->dsp, DSP_SET_FREQUENCY, VOICE_SAMPLE_RATE);
212 dsp_configure(td->dsp, DSP_SET_SAMPLE_DEPTH, VOICE_SAMPLE_DEPTH); 264 dsp_configure(td->dsp, DSP_SET_SAMPLE_DEPTH, VOICE_SAMPLE_DEPTH);
213 dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO); 265 dsp_configure(td->dsp, DSP_SET_STEREO_MODE, STEREO_MONO);
266
267 mixer_channel_set_amplitude(PCM_MIXER_CHAN_VOICE, MIX_AMP_UNITY);
268 td->quiet_counter = 0;
214} 269}
215 270
216/* Voice thread message processing */ 271/* Voice thread message processing */
@@ -222,7 +277,6 @@ static void voice_message(struct voice_thread_data *td)
222 { 277 {
223 case Q_VOICE_PLAY: 278 case Q_VOICE_PLAY:
224 LOGFQUEUE("voice < Q_VOICE_PLAY"); 279 LOGFQUEUE("voice < Q_VOICE_PLAY");
225 /* Put up a block for completion signal */
226 voice_done = false; 280 voice_done = false;
227 281
228 /* Copy the clip info */ 282 /* Copy the clip info */
@@ -239,12 +293,17 @@ static void voice_message(struct voice_thread_data *td)
239 /* Boost CPU now */ 293 /* Boost CPU now */
240 trigger_cpu_boost(); 294 trigger_cpu_boost();
241 } 295 }
242 else if (!playback_is_playing()) 296 else
243 { 297 {
244 /* Just voice, stop any clip still playing */ 298 /* Stop any clip still playing */
245 pcmbuf_play_stop(); 299 voice_stop_playback();
246 } 300 }
247 301
302 /* Make audio play more softly and set delay to return to normal
303 playback level */
304 pcmbuf_soft_mode(true);
305 td->quiet_counter = QUIET_COUNT;
306
248 /* Clean-start the decoder */ 307 /* Clean-start the decoder */
249 td->st = speex_decoder_init(&speex_wb_mode); 308 td->st = speex_decoder_init(&speex_wb_mode);
250 309
@@ -255,30 +314,32 @@ static void voice_message(struct voice_thread_data *td)
255 td->state = TSTATE_DECODE; 314 td->state = TSTATE_DECODE;
256 return; 315 return;
257 316
258 case Q_VOICE_STOP: 317 case SYS_TIMEOUT:
259 LOGFQUEUE("voice < Q_VOICE_STOP: %ld", (long)td->ev.data); 318 if (voice_unplayed_frames())
319 {
320 /* Waiting for PCM to finish */
321 break;
322 }
260 323
261 if (td->ev.data != 0 && !playback_is_playing()) 324 /* Drop through and stop the first time after clip runs out */
325 if (td->quiet_counter-- != QUIET_COUNT)
262 { 326 {
263 /* If not playing, it's just voice so stop pcm playback */ 327 if (td->quiet_counter <= 0)
264 pcmbuf_play_stop(); 328 pcmbuf_soft_mode(false);
329
330 break;
265 } 331 }
266 332
267 /* Cancel boost */ 333 /* Fall-through */
268 cancel_cpu_boost(); 334 case Q_VOICE_STOP:
335 LOGFQUEUE("voice < Q_VOICE_STOP");
269 336
270 td->state = TSTATE_STOPPED; 337 td->state = TSTATE_STOPPED;
271 voice_done = true; 338 voice_done = true;
272 break;
273
274 case Q_VOICE_STATE:
275 LOGFQUEUE("voice < Q_VOICE_STATE");
276 queue_reply(&voice_queue, td->state);
277
278 if (td->state == TSTATE_STOPPED)
279 break; /* Not in a playback state */
280 339
281 return; 340 cancel_cpu_boost();
341 voice_stop_playback();
342 break;
282 343
283 default: 344 default:
284 /* Default messages get a reply and thread continues with no 345 /* Default messages get a reply and thread continues with no
@@ -286,20 +347,24 @@ static void voice_message(struct voice_thread_data *td)
286 LOGFQUEUE("voice < default"); 347 LOGFQUEUE("voice < default");
287 348
288 if (td->state == TSTATE_STOPPED) 349 if (td->state == TSTATE_STOPPED)
289 break; /* Not in playback state */ 350 break; /* Not in (active) playback state */
290 351
291 queue_reply(&voice_queue, 0); 352 queue_reply(&voice_queue, 0);
292 return; 353 return;
293 } 354 }
294 355
295 queue_wait(&voice_queue, &td->ev); 356 if (td->quiet_counter > 0)
357 queue_wait_w_tmo(&voice_queue, &td->ev, HZ/10);
358 else
359 queue_wait(&voice_queue, &td->ev);
296 } 360 }
297} 361}
298 362
299/* Voice thread entrypoint */ 363/* Voice thread entrypoint */
300static void voice_thread(void) 364static void NORETURN_ATTR voice_thread(void)
301{ 365{
302 struct voice_thread_data td; 366 struct voice_thread_data td;
367 char *dest;
303 368
304 voice_data_init(&td); 369 voice_data_init(&td);
305 370
@@ -361,19 +426,10 @@ static void voice_thread(void)
361 } 426 }
362 427
363 /* If all clips are done and not playing, force pcm playback. */ 428 /* If all clips are done and not playing, force pcm playback. */
364 if (!pcm_is_playing()) 429 voice_start_playback();
365 pcmbuf_play_start(); 430
366 431 td.state = TSTATE_STOPPED;
367 /* Synthesize a stop request */ 432 td.ev.id = SYS_TIMEOUT;
368 /* NOTE: We have no way to know when the pcm data placed in the
369 * buffer is actually consumed and playback has reached the end
370 * so until the info is available or inferred somehow, this will
371 * not be accurate and the stopped signal will come too soon.
372 * ie. You may not hear the "Shutting Down" splash even though
373 * it waits for voice to stop. */
374 td.ev.id = Q_VOICE_STOP;
375 td.ev.data = 0; /* Let PCM drain by itself */
376 yield();
377 goto message_process; 433 goto message_process;
378 } 434 }
379 435
@@ -385,62 +441,39 @@ static void voice_thread(void)
385 td.src[1] = NULL; 441 td.src[1] = NULL;
386 td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead); 442 td.lookahead -= MIN(VOICE_FRAME_SIZE, td.lookahead);
387 443
444 if (td.count <= 0)
445 continue;
446
447 td.state = TSTATE_BUFFER_INSERT;
448
388 buffer_insert: 449 buffer_insert:
389 /* Process the PCM samples in the DSP and send out for mixing */ 450 /* Process the PCM samples in the DSP and send out for mixing */
390 td.state = TSTATE_BUFFER_INSERT;
391 451
392 while (td.count > 0) 452 while (1)
393 { 453 {
394 int out_count = dsp_output_count(td.dsp, td.count); 454 if (!queue_empty(&voice_queue))
395 int inp_count; 455 goto message_wait;
396 char *dest;
397 456
398 while (1) 457 if ((dest = (char *)voice_buf_get()) != NULL)
399 {
400 if (!queue_empty(&voice_queue))
401 goto message_wait;
402
403 if ((dest = pcmbuf_request_voice_buffer(&out_count)) != NULL)
404 break;
405
406 yield();
407 }
408
409 /* Get the real input_size for output_size bytes, guarding
410 * against resampling buffer overflows. */
411 inp_count = dsp_input_count(td.dsp, out_count);
412
413 if (inp_count <= 0)
414 break;
415
416 /* Input size has grown, no error, just don't write more than
417 * length */
418 if (inp_count > td.count)
419 inp_count = td.count;
420
421 out_count = dsp_process(td.dsp, dest, td.src, inp_count);
422
423 if (out_count <= 0)
424 break; 458 break;
425 459
426 pcmbuf_write_voice_complete(out_count); 460 yield();
427 td.count -= inp_count;
428 } 461 }
429 462
430 yield(); 463 voice_buf_commit(dsp_process(td.dsp, dest, td.src, td.count)
464 * sizeof (int32_t));
431 } /* end while */ 465 } /* end while */
432} /* voice_thread */ 466}
433 467
434/* Initialize all synchronization objects create the thread */ 468/* Initialize all synchronization objects create the thread */
435void voice_thread_init(void) 469void voice_thread_init(void)
436{ 470{
437 logf("Starting voice thread"); 471 logf("Starting voice thread");
438 queue_init(&voice_queue, false); 472 queue_init(&voice_queue, false);
439 mutex_init(&voice_mutex);
440 473
441 voice_thread_id = create_thread(voice_thread, voice_stack, 474 voice_thread_id = create_thread(voice_thread, voice_stack,
442 sizeof(voice_stack), CREATE_THREAD_FROZEN, 475 sizeof(voice_stack), CREATE_THREAD_FROZEN,
443 voice_thread_name IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU)); 476 voice_thread_name IF_PRIO(, PRIORITY_VOICE) IF_COP(, CPU));
444 477
445 queue_enable_queue_send(&voice_queue, &voice_queue_sender_list, 478 queue_enable_queue_send(&voice_queue, &voice_queue_sender_list,
446 voice_thread_id); 479 voice_thread_id);
@@ -457,6 +490,18 @@ void voice_thread_resume(void)
457/* Set the voice thread priority */ 490/* Set the voice thread priority */
458void voice_thread_set_priority(int priority) 491void voice_thread_set_priority(int priority)
459{ 492{
493 if (priority > PRIORITY_VOICE)
494 priority = PRIORITY_VOICE;
495
460 thread_set_priority(voice_thread_id, priority); 496 thread_set_priority(voice_thread_id, priority);
461} 497}
462#endif 498#endif
499
500/* Initialize voice PCM buffer and return size, allocated from the end */
501size_t voicebuf_init(unsigned char *bufend)
502{
503 size_t size = VOICE_FRAMES * VOICE_PCM_FRAME_SIZE;
504 cur_buf_out = cur_buf_in = 0;
505 voicebuf = (uint32_t (*)[VOICE_PCM_FRAME_COUNT])(bufend - size);
506 return size;
507}
diff --git a/apps/voice_thread.h b/apps/voice_thread.h
index 4359825dd7..1529f7efe8 100644
--- a/apps/voice_thread.h
+++ b/apps/voice_thread.h
@@ -29,8 +29,13 @@ bool mp3_is_playing(void);
29 29
30void voice_wait(void); 30void voice_wait(void);
31void voice_stop(void); 31void voice_stop(void);
32
32void voice_thread_init(void); 33void voice_thread_init(void);
33void voice_thread_resume(void); 34void voice_thread_resume(void);
35#ifdef HAVE_PRIORITY_SCHEDULING
34void voice_thread_set_priority(int priority); 36void voice_thread_set_priority(int priority);
37#endif
38
39size_t voicebuf_init(unsigned char *bufend);
35 40
36#endif /* VOICE_THREAD_H */ 41#endif /* VOICE_THREAD_H */