summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-06-29 06:37:04 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-06-29 06:37:04 +0000
commita2b6703a369f6cdbfec1f150c408dadc877631fb (patch)
tree3145a8c1372c44711d38feefeba39c7d4098f139 /apps
parent8411614b8a068a4f274c3841aa55aab1df1bc246 (diff)
downloadrockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.tar.gz
rockbox-a2b6703a369f6cdbfec1f150c408dadc877631fb.zip
Commit FS#12150 - Fully-functional audio mixer - and finally whip old limitations about playback of voice and other sounds when paused. Channels are independent in state and amplitude. Fade on stop/pause is handled by the channel's volume control rather than global volume which means it now works from anywhere. Opens up the possibility of plugin sounds during music playback by merely adding an additional channel enum. If any PCM drivers were not properly modified, see one of the last comments in the task for a description of the simple change that is expected. Some params are tunable in firmware/export/pcm-mixer.h as well.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30097 a1c6a512-1295-4272-9138-f99709370657
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 */