summaryrefslogtreecommitdiff
path: root/apps/pcmbuf.c
diff options
context:
space:
mode:
authorBrandon Low <lostlogic@rockbox.org>2006-02-07 20:38:55 +0000
committerBrandon Low <lostlogic@rockbox.org>2006-02-07 20:38:55 +0000
commit413da2a3d93d989d4474edad437ff67888487cb9 (patch)
treeecf938aa0aedc92db749be69e62648050f2fd712 /apps/pcmbuf.c
parent566ce5f95163f8bbb7357dc7353bb132365f7b6e (diff)
downloadrockbox-413da2a3d93d989d4474edad437ff67888487cb9.zip
rockbox-413da2a3d93d989d4474edad437ff67888487cb9.tar.gz
rockbox-413da2a3d93d989d4474edad437ff67888487cb9.tar.bz2
rockbox-413da2a3d93d989d4474edad437ff67888487cb9.tar.xz
Rework PCM buffer
* Linked list instead of static array buffer pointers * Variable sized chunks * Improved mix handling * Reduction in duplicated code * Reduced IRAM usage w/o sacrificing performance * Converted to almost entirely unsigned math * Add pause function to reduce pcm_* exposure to playback. This WILL break playback on the iPod until linuxstb makes a followup commit. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8612 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/pcmbuf.c')
-rw-r--r--apps/pcmbuf.c711
1 files changed, 388 insertions, 323 deletions
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 57058a3..cfb297e 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -36,32 +36,32 @@
#include "audio.h"
#include "dsp.h"
-#define CHUNK_SIZE 32768
-/* Must be a power of 2 */
-#define NUM_PCM_BUFFERS 128
-#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
-/* Watermark level at 1s. */
#define PCMBUF_WATERMARK (NATIVE_FREQUENCY * 4 * 1)
-/* Audio buffer related settings. */
-static long pcmbuf_size = 0; /* Size of the PCM buffer. */
-static char *audiobuffer;
-static long audiobuffer_pos; /* Current audio buffer write index. */
-long audiobuffer_free IDATA_ATTR; /* Amount of bytes left in the buffer. */
-static long audiobuffer_fillpos; /* Amount audiobuffer_pos will be increased.*/
-static char *guardbuf;
+/* Size of the PCM buffer. */
+static size_t pcmbuf_size IDATA_ATTR = 0;
-static void (*pcmbuf_event_handler)(void);
+static char *audiobuffer IDATA_ATTR;
+/* Current audio buffer write index. */
+static size_t audiobuffer_pos IDATA_ATTR;
+/* Amount of bytes left in the buffer. */
+size_t audiobuffer_free IDATA_ATTR;
+/* Amount audiobuffer_pos will be increased.*/
+static size_t audiobuffer_fillpos IDATA_ATTR;
+static char *guardbuf IDATA_ATTR;
+
+static void (*pcmbuf_event_handler)(void) IDATA_ATTR;
+static void (*position_callback)(size_t size) IDATA_ATTR;
/* Crossfade related. */
-static int crossfade_mode;
-static bool crossfade_enabled;
-static bool crossfade_active;
-static bool crossfade_init;
-static int crossfade_pos;
-static int crossfade_rem;
+static int crossfade_mode IDATA_ATTR;
+static bool crossfade_enabled IDATA_ATTR;
+static bool crossfade_active IDATA_ATTR;
+static bool crossfade_init IDATA_ATTR;
+static size_t crossfade_pos IDATA_ATTR;
+static size_t crossfade_rem IDATA_ATTR;
-static struct mutex pcmbuf_mutex;
+static struct mutex pcmbuf_mutex IDATA_ATTR;
/* Crossfade modes. If CFM_CROSSFADE is selected, normal
* crossfader will activate. Selecting CFM_FLUSH is a special
@@ -73,32 +73,44 @@ enum {
CFM_FLUSH
};
-static int crossfade_fade_in_amount;
-static int crossfade_fade_in_rem;
+static size_t crossfade_fade_in_amount IDATA_ATTR;
+static size_t crossfade_fade_in_rem IDATA_ATTR;
+
/* Structure we can use to queue pcm chunks in memory to be played
* by the driver code. */
struct pcmbufdesc
{
void *addr;
- int size;
+ size_t size;
+ struct pcmbufdesc* link;
/* Call this when the buffer has been played */
void (*callback)(void);
-} pcmbuffers[NUM_PCM_BUFFERS] IDATA_ATTR; /* Do we really need IRAM for this? */
-
-static int pcmbuf_read_index;
-static int pcmbuf_write_index;
-static int pcmbuf_unplayed_bytes IDATA_ATTR;
-static int pcmbuf_mix_used_bytes;
-static int pcmbuf_watermark;
-static void pcmbuf_under_watermark(int bytes_left);
-static int pcmbuf_num_used_buffers(void);
-static void (*position_callback)(int size);
-static int last_chunksize;
-static long mixpos = 0;
+};
+
+static size_t pcmbuf_descsize;
+static struct pcmbufdesc *pcmbuf_read IDATA_ATTR;
+static struct pcmbufdesc *pcmbuf_read_end IDATA_ATTR;
+static struct pcmbufdesc *pcmbuf_write IDATA_ATTR;
+static struct pcmbufdesc *pcmbuf_write_end IDATA_ATTR;
+static size_t last_chunksize IDATA_ATTR;
+static size_t pcmbuf_unplayed_bytes IDATA_ATTR;
+static size_t pcmbuf_mix_used_bytes IDATA_ATTR;
+static size_t pcmbuf_watermark IDATA_ATTR;
+static size_t mixpos IDATA_ATTR = 0;
static bool low_latency_mode = false;
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+/* Helpful macros for use in conditionals this assumes some of the above
+ * static variable names */
+#define NEED_FLUSH(position) \
+ (audiobuffer_fillpos > PCMBUF_TARGET_CHUNK || position >= pcmbuf_size)
+#define LOW_DATA(quarter_secs) \
+ (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
+
+static void pcmbuf_flush_audio(void);
+static void pcmbuf_under_watermark(void);
+
+#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && !defined(SIMULATOR)
static bool boost_mode;
void pcmbuf_boost(bool state)
@@ -106,7 +118,7 @@ void pcmbuf_boost(bool state)
static bool boost_state = false;
if (crossfade_init || crossfade_active || boost_mode)
- return ;
+ return;
if (state != boost_state) {
cpu_boost(state);
@@ -122,106 +134,129 @@ void pcmbuf_set_boost_mode(bool state)
}
#endif
-static int pcmbuf_num_used_buffers(void)
+#define CALL_IF_EXISTS(function, args...) if (function) function(args)
+/* This function has 2 major logical parts (separated by brackets both for
+ * readability and variable scoping). The first part performs the
+ * operastions related to finishing off the last buffer we fed to the DMA.
+ * The second part performs the operations involved in sending a new buffer
+ * to the DMA. Finally the function checks the status of the buffer and
+ * boosts if necessary */
+static void pcmbuf_callback(unsigned char** start, size_t* size) ICODE_ATTR;
+static void pcmbuf_callback(unsigned char** start, size_t* size)
{
- return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
-}
-
-static void pcmbuf_callback(unsigned char** start, long* size) ICODE_ATTR;
-static void pcmbuf_callback(unsigned char** start, long* size)
-{
- struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
-
- if (position_callback) {
- position_callback(last_chunksize);
- }
-
- pcmbuf_unplayed_bytes -= last_chunksize;
- audiobuffer_free += last_chunksize;
-
- if(desc->size == 0)
{
- /* The buffer is finished, call the callback function */
- if(desc->callback)
- desc->callback();
+ struct pcmbufdesc *pcmbuf_current = pcmbuf_read;
+ /* Take the finished buffer out of circulation */
+ pcmbuf_read = pcmbuf_current->link;
- /* Advance to the next buffer */
- pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
- desc = &pcmbuffers[pcmbuf_read_index];
- }
-
- if(pcmbuf_num_used_buffers())
- {
-
- *start = desc->addr;
- *size = desc->size;
+ {
+ size_t finished_size = last_chunksize;
+ audiobuffer_free += finished_size;
- /* Update the buffer descriptor */
- desc->addr += desc->size;
- desc->size = 0;
- }
- else
- {
- /* No more buffers */
- *size = 0;
- if (pcmbuf_event_handler)
- pcmbuf_event_handler();
- }
+ /* The buffer is finished, call the callback functions */
+ CALL_IF_EXISTS(position_callback, finished_size);
+ }
+ CALL_IF_EXISTS(pcmbuf_current->callback);
- last_chunksize = *size;
+ /* Put the finished buffer back into circulation */
+ pcmbuf_write_end->link = pcmbuf_current;
+ pcmbuf_write_end = pcmbuf_current;
+ }
- if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
{
- pcmbuf_under_watermark(pcmbuf_unplayed_bytes);
+ /* Send the new buffer to the pcm */
+ struct pcmbufdesc *pcmbuf_new = pcmbuf_read;
+ size_t *realsize = size;
+ unsigned char** realstart = start;
+ if(pcmbuf_new)
+ {
+ size_t current_size = pcmbuf_new->size;
+ pcmbuf_unplayed_bytes -= current_size;
+ *realsize = current_size;
+ last_chunksize = current_size;
+ *realstart = pcmbuf_new->addr;
+ }
+ else
+ {
+ /* No more buffers */
+ last_chunksize = 0;
+ *realsize = 0;
+ *realstart = NULL;
+ CALL_IF_EXISTS(pcmbuf_event_handler);
+ }
}
+
+ if(pcmbuf_unplayed_bytes <= pcmbuf_watermark) pcmbuf_under_watermark();
}
-void pcmbuf_set_position_callback(void (*callback)(int size)) {
+void pcmbuf_set_position_callback(void (*callback)(size_t size))
+{
position_callback = callback;
}
-static void pcmbuf_set_watermark_bytes(int numbytes)
+static void pcmbuf_set_watermark_bytes(size_t numbytes)
{
pcmbuf_watermark = numbytes;
}
-bool pcmbuf_add_chunk(void *addr, int size, void (*callback)(void))
-{
- /* We don't use the last buffer, since we can't see the difference
- between the full and empty condition */
- if(pcmbuf_num_used_buffers() < (NUM_PCM_BUFFERS - 2))
- {
- pcmbuffers[pcmbuf_write_index].addr = addr;
- pcmbuffers[pcmbuf_write_index].size = size;
- pcmbuffers[pcmbuf_write_index].callback = callback;
- pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK;
- pcmbuf_unplayed_bytes += size;
- pcmbuf_mix_used_bytes = MAX(0, pcmbuf_mix_used_bytes - size);
- return true;
+/* This is really just part of pcmbuf_flush_fillpos, but is easier to keep
+ * in a separate function for the moment */
+static inline void pcmbuf_add_chunk(void)
+{
+ register size_t size = audiobuffer_fillpos;
+ /* Grab the next description to write, and change the write pointer */
+ register struct pcmbufdesc *pcmbuf_current = pcmbuf_write;
+ pcmbuf_write = pcmbuf_current->link;
+ /* Fill in the values in the new buffer chunk */
+ pcmbuf_current->addr = &audiobuffer[audiobuffer_pos];
+ pcmbuf_current->size = size;
+ pcmbuf_current->callback = pcmbuf_event_handler;
+ pcmbuf_current->link = NULL;
+ /* This is single use only */
+ pcmbuf_event_handler = NULL;
+ if (pcmbuf_read) {
+ /* If there is already a read buffer setup, add to it */
+ pcmbuf_read_end->link = pcmbuf_current;
+ } else {
+ /* Otherwise create the buffer */
+ pcmbuf_read = pcmbuf_current;
}
+ /* This is now the last buffer to read */
+ pcmbuf_read_end = pcmbuf_current;
+
+ /* Update bytes counters */
+ pcmbuf_unplayed_bytes += size;
+ if (pcmbuf_mix_used_bytes > size)
+ pcmbuf_mix_used_bytes -= size;
else
- return false;
+ pcmbuf_mix_used_bytes = 0;
+
+ audiobuffer_pos += size;
+ if (audiobuffer_pos >= pcmbuf_size)
+ audiobuffer_pos = 0;
+
+ audiobuffer_fillpos = 0;
}
-static void pcmbuf_under_watermark(int bytes_left)
+static void pcmbuf_under_watermark(void)
{
/* Fill audio buffer by boosting cpu */
pcmbuf_boost(true);
- if (bytes_left <= CHUNK_SIZE * 2 && crossfade_mode != CFM_FLUSH)
+ /* Disable crossfade if < .5s of audio */
+ if (LOW_DATA(2) && crossfade_mode != CFM_FLUSH)
crossfade_active = false;
}
-void pcmbuf_add_event(void (*event_handler)(void))
+void pcmbuf_set_event_handler(void (*event_handler)(void))
{
pcmbuf_event_handler = event_handler;
}
unsigned int pcmbuf_get_latency(void)
{
- int latency = (pcmbuf_unplayed_bytes + pcm_get_bytes_waiting())
- * 1000 / 4 / 44100;
-
- return latency<0?0:latency;
+ /* Be careful how this calculation is rearranted, it's easy to overflow */
+ size_t bytes = pcmbuf_unplayed_bytes + pcm_get_bytes_waiting();
+ return bytes / 4 / (NATIVE_FREQUENCY/1000);
}
void pcmbuf_set_low_latency(bool state)
@@ -231,19 +266,17 @@ void pcmbuf_set_low_latency(bool state)
bool pcmbuf_is_lowdata(void)
{
- if (!pcm_is_playing() || pcm_is_paused() || crossfade_init || crossfade_active)
+ if (!pcm_is_playing() || pcm_is_paused() ||
+ crossfade_init || crossfade_active)
return false;
-
- /* 0.5s. */
- if (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * 4 / 2)
- return true;
-
- return false;
+
+ /* 0.5 seconds of buffer is low data */
+ return LOW_DATA(2);
}
bool pcmbuf_crossfade_init(bool manual_skip)
{
- if (pcmbuf_size - audiobuffer_free < CHUNK_SIZE * 8
+ if (pcmbuf_unplayed_bytes < PCMBUF_TARGET_CHUNK * 8
|| !pcmbuf_is_crossfade_enabled()
|| crossfade_active || crossfade_init || low_latency_mode) {
pcmbuf_flush_audio();
@@ -267,18 +300,19 @@ bool pcmbuf_crossfade_init(bool manual_skip)
void pcmbuf_play_stop(void)
{
mutex_lock(&pcmbuf_mutex);
-
/** Prevent a very tiny pop from happening by muting audio
* until dma has been initialized. */
pcm_mute(true);
pcm_play_stop();
pcm_mute(false);
- last_chunksize = 0;
pcmbuf_unplayed_bytes = 0;
pcmbuf_mix_used_bytes = 0;
- pcmbuf_read_index = 0;
- pcmbuf_write_index = 0;
+ if (pcmbuf_read) {
+ pcmbuf_write_end->link = pcmbuf_read;
+ pcmbuf_write_end = pcmbuf_read_end;
+ pcmbuf_read = pcmbuf_read_end = NULL;
+ }
audiobuffer_pos = 0;
audiobuffer_fillpos = 0;
audiobuffer_free = pcmbuf_size;
@@ -291,19 +325,53 @@ void pcmbuf_play_stop(void)
mutex_unlock(&pcmbuf_mutex);
}
-void pcmbuf_init(long bufsize)
+int pcmbuf_used_descs(void) {
+ struct pcmbufdesc *pcmbuf_temp = pcmbuf_read;
+ unsigned int i = 0;
+ while (pcmbuf_temp) {
+ pcmbuf_temp = pcmbuf_temp->link;
+ i++;
+ }
+ return i;
+}
+
+int pcmbuf_descs(void) {
+ return pcmbuf_size / PCMBUF_MINAVG_CHUNK;
+}
+
+size_t get_pcmbuf_descsize(void) {
+ return pcmbuf_descsize;
+}
+
+static void pcmbuf_init_pcmbuffers(void) {
+ struct pcmbufdesc *next = pcmbuf_write;
+ next++;
+ pcmbuf_write_end = pcmbuf_write;
+ while ((void *)next < (void *)audiobufend) {
+ pcmbuf_write_end->link=next;
+ pcmbuf_write_end=next;
+ next++;
+ }
+}
+
+/* Initialize the pcmbuffer the structure looks like this:
+ * ...CODECBUFFER|---------PCMBUF---------|GUARDBUF|DESCS| */
+void pcmbuf_init(size_t bufsize)
{
mutex_init(&pcmbuf_mutex);
pcmbuf_size = bufsize;
- audiobuffer = (char *)&audiobuf[(audiobufend - audiobuf) -
- pcmbuf_size - PCMBUF_GUARD];
+ pcmbuf_descsize = pcmbuf_descs()*sizeof(struct pcmbufdesc);
+ audiobuffer = (char *)&audiobuf[(audiobufend - audiobuf) -
+ (pcmbuf_size + PCMBUF_FADE_CHUNK + pcmbuf_descsize)];
guardbuf = &audiobuffer[pcmbuf_size];
+ pcmbuf_write = (struct pcmbufdesc *)(&guardbuf[PCMBUF_FADE_CHUNK]);
+ pcmbuf_init_pcmbuffers();
position_callback = NULL;
pcmbuf_event_handler = NULL;
pcmbuf_play_stop();
}
-long pcmbuf_get_bufsize(void)
+size_t pcmbuf_get_bufsize(void)
{
return pcmbuf_size;
}
@@ -311,7 +379,7 @@ long pcmbuf_get_bufsize(void)
/** Initialize a track switch so that audio playback will not stop but
* the switch to next track would happen as soon as possible.
*/
-void pcmbuf_flush_audio(void)
+static void pcmbuf_flush_audio(void)
{
if (crossfade_init || crossfade_active || !pcm_is_playing()) {
pcmbuf_play_stop();
@@ -323,66 +391,71 @@ void pcmbuf_flush_audio(void)
crossfade_init = true;
}
+void pcmbuf_pause(bool pause) {
+ pcm_mute(pause);
+ pcm_play_pause(!pause);
+ pcmbuf_boost(!pause);
+}
+
/* Force playback. */
void pcmbuf_play_start(void)
{
+ mutex_lock(&pcmbuf_mutex);
+
if (!pcm_is_playing() && pcmbuf_unplayed_bytes)
{
/** Prevent a very tiny pop from happening by muting audio
* until dma has been initialized. */
pcm_mute(true);
- pcm_play_data(pcmbuf_callback);
+ last_chunksize = pcmbuf_read->size;
+ pcmbuf_unplayed_bytes -= last_chunksize;
+ pcm_play_data(pcmbuf_callback,
+ (unsigned char *)pcmbuf_read->addr, last_chunksize);
/* Now unmute the audio. */
pcm_mute(false);
}
+
+ mutex_unlock(&pcmbuf_mutex);
}
/**
* Commit samples waiting to the pcm buffer.
*/
-void pcmbuf_flush_fillpos(void)
+static void pcmbuf_flush_fillpos(void)
{
- int copy_n;
-
mutex_lock(&pcmbuf_mutex);
-
- copy_n = MIN(audiobuffer_fillpos, CHUNK_SIZE);
-
- if (copy_n) {
- while (!pcmbuf_add_chunk(&audiobuffer[audiobuffer_pos],
- copy_n, pcmbuf_event_handler)) {
+
+ if (audiobuffer_fillpos) {
+ /* Never use the last buffer descriptor */
+ while (pcmbuf_write == pcmbuf_write_end) {
+ logf("pcmbuf_flush_fillpos no descriptors");
+ /* Deboost to let the playback catchup */
pcmbuf_boost(false);
+ /* Let someone else have fun in the meantime */
sleep(1);
/* This is a fatal error situation that should never happen. */
if (!pcm_is_playing()) {
- logf("pcm_flush_fillpos error");
+ logf("pcmbuf_flush_fillpos error");
pcmbuf_play_start();
- mutex_unlock(&pcmbuf_mutex);
return ;
}
}
- position_callback = NULL;
- pcmbuf_event_handler = NULL;
- audiobuffer_pos += copy_n;
- if (audiobuffer_pos >= pcmbuf_size)
- audiobuffer_pos -= pcmbuf_size;
- audiobuffer_free -= copy_n;
- audiobuffer_fillpos -= copy_n;
+ pcmbuf_add_chunk();
}
-
+
mutex_unlock(&pcmbuf_mutex);
}
/**
* Completely process the crossfade fade out effect with current pcm buffer.
*/
-static void crossfade_process_buffer(
- int fade_in_delay, int fade_out_delay, int fade_out_rem)
+static void crossfade_process_buffer(unsigned int fade_in_delay,
+ unsigned int fade_out_delay, size_t fade_out_rem)
{
- int amount;
- int pos;
+ size_t amount;
+ size_t pos;
short *buf;
/* Fade out the entire current buffer according to settings. */
@@ -391,12 +464,12 @@ static void crossfade_process_buffer(
while (fade_out_rem > 0 && crossfade_mode == CFM_CROSSFADE)
{
- int blocksize = MIN(8192, fade_out_rem);
+ size_t blocksize = MIN(8192, fade_out_rem);
int factor = (fade_out_rem<<8)/amount;
/* Prevent pcmbuffer from wrapping. */
- if (pos >= pcmbuf_size)
- pos -= pcmbuf_size;
+ if (pos >= pcmbuf_size) pos -= pcmbuf_size;
+
blocksize = MIN((pcmbuf_size - pos)/2, blocksize);
buf = (short *)&audiobuffer[pos];
@@ -408,7 +481,6 @@ static void crossfade_process_buffer(
*buf++;
blocksize--;
}
- //yield();
}
/* And finally set the mixing position where we should start fading in. */
@@ -425,12 +497,13 @@ static void crossfade_process_buffer(
*/
static void crossfade_start(void)
{
- int bytesleft = pcmbuf_unplayed_bytes;
- int fade_out_rem = 0, fade_out_delay = 0;
- int fade_in_delay = 0;
+ size_t fade_out_rem = 0;
+ unsigned int fade_out_delay = 0;
+ unsigned fade_in_delay = 0;
crossfade_init = 0;
- if (bytesleft < CHUNK_SIZE * 4) {
+ /* Reject crossfade if less than .5s of data */
+ if (LOW_DATA(2)) {
logf("crossfade rejected");
pcmbuf_play_stop();
return ;
@@ -438,18 +511,16 @@ static void crossfade_start(void)
logf("crossfade_start");
pcmbuf_boost(true);
- while (audiobuffer_fillpos != 0)
- pcmbuf_flush_fillpos();
+ pcmbuf_flush_fillpos();
crossfade_active = true;
crossfade_pos = audiobuffer_pos;
+ /* Initialize the crossfade buffer size to all of the buffered data that
+ * has not yet been sent to the DMA */
+ crossfade_rem = pcmbuf_unplayed_bytes / 2;
switch (crossfade_mode) {
case CFM_MIX:
case CFM_CROSSFADE:
- /* Initialize the crossfade buffer size. */
- // FIXME: Crashes unless we use CHUNK_SIZE here
- crossfade_rem = (bytesleft - (CHUNK_SIZE * 2))/2;
-
/* Get fade out delay from settings. */
fade_out_delay = NATIVE_FREQUENCY
* global_settings.crossfade_fade_out_delay * 2;
@@ -479,23 +550,20 @@ static void crossfade_start(void)
* global_settings.crossfade_fade_in_delay * 2;
/* Decrease the fade out delay if necessary. */
- fade_out_delay += MIN(crossfade_rem -
- fade_out_rem -
- fade_out_delay, 0);
- if (fade_out_delay < 0)
- fade_out_delay = 0;
+ if (crossfade_rem < fade_out_rem + fade_out_delay)
+ fade_out_delay -=
+ (fade_out_rem + fade_out_delay) - crossfade_rem;
break ;
case CFM_FLUSH:
- crossfade_rem = (bytesleft - CHUNK_SIZE) /2;
crossfade_fade_in_rem = 0;
crossfade_fade_in_amount = 0;
break ;
}
- crossfade_pos -= crossfade_rem*2;
- if (crossfade_pos < 0)
+ if (crossfade_pos < crossfade_rem * 2)
crossfade_pos += pcmbuf_size;
+ crossfade_pos -= crossfade_rem*2;
if (crossfade_mode != CFM_FLUSH) {
/* Process the fade out part of the crossfade. */
@@ -508,29 +576,35 @@ static void crossfade_start(void)
* Fades in samples passed to the function and inserts them
* to the pcm buffer.
*/
-static void fade_insert(const short *inbuf, int length)
+static void fade_insert(const short *inbuf, size_t length)
{
- int copy_n;
+ size_t copy_n;
int factor;
- int i, samples;
+ unsigned int i, samples;
short *buf;
- factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem)
- <<8)/crossfade_fade_in_amount;
+ factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem)<<8)
+ /crossfade_fade_in_amount;
- while (audiobuffer_free < length + audiobuffer_fillpos
- + CHUNK_SIZE)
+ while (audiobuffer_free < length)
{
pcmbuf_boost(false);
sleep(1);
}
+ audiobuffer_free -= length;
while (length > 0) {
- copy_n = MIN(length, pcmbuf_size - audiobuffer_pos -
- audiobuffer_fillpos);
- copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n);
+ unsigned int audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
+ /* Flush as needed */
+ if (NEED_FLUSH(audiobuffer_index))
+ {
+ pcmbuf_flush_fillpos();
+ audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
+ }
+
+ copy_n = MIN(length, pcmbuf_size - audiobuffer_index);
- buf = (short *)&audiobuffer[audiobuffer_pos+audiobuffer_fillpos];
+ buf = (short *)&audiobuffer[audiobuffer_index];
samples = copy_n / 2;
for (i = 0; i < samples; i++)
buf[i] = (inbuf[i] * factor) >> 8;
@@ -538,36 +612,30 @@ static void fade_insert(const short *inbuf, int length)
inbuf += samples;
audiobuffer_fillpos += copy_n;
length -= copy_n;
-
- /* Pre-buffer to meet CHUNK_SIZE requirement */
- if (audiobuffer_fillpos < CHUNK_SIZE && length == 0) {
- break ;
- }
-
- pcmbuf_flush_fillpos();
}
}
/**
* Fades in buf2 and mixes it with buf.
*/
-static __inline
-int crossfade(short *buf, const short *buf2, int length)
+static int crossfade(short *buf, const short *buf2, unsigned int length)
{
- int size, i;
- int size_insert = 0;
+ size_t size;
+ unsigned int i;
+ size_t size_insert = 0;
int factor;
- size = MAX(0, MIN(length, crossfade_rem));
+ size = MIN(length, crossfade_rem);
switch (crossfade_mode) {
/* Fade in the current stream and mix it. */
case CFM_MIX:
case CFM_CROSSFADE:
- factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem)
- <<8)/crossfade_fade_in_amount;
+ factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem)<<8) /
+ crossfade_fade_in_amount;
for (i = 0; i < size; i++) {
- buf[i] = MIN(MAX(buf[i] + ((buf2[i] * factor) >> 8), -32768), 32767);
+ buf[i] = MIN(32767, MAX(-32768,
+ buf[i] + ((buf2[i] * factor) >> 8)));
}
break ;
@@ -580,25 +648,65 @@ int crossfade(short *buf, const short *buf2, int length)
break ;
}
- crossfade_fade_in_rem = MAX(0, crossfade_fade_in_rem - size);
+ if (crossfade_fade_in_rem > size)
+ crossfade_fade_in_rem = crossfade_fade_in_rem - size;
+ else
+ crossfade_fade_in_rem = 0;
+
crossfade_rem -= size;
- if (crossfade_rem <= 0)
+ if (crossfade_rem == 0)
{
if (crossfade_fade_in_rem > 0 && crossfade_fade_in_amount > 0)
{
- size_insert = MAX(0, MIN(crossfade_fade_in_rem, length - size));
+ size_insert = MIN(crossfade_fade_in_rem, length - size);
fade_insert(&buf2[size], size_insert*2);
crossfade_fade_in_rem -= size_insert;
}
- if (crossfade_fade_in_rem <= 0)
+ if (crossfade_fade_in_rem == 0)
crossfade_active = false;
}
return size + size_insert;
}
-static bool prepare_insert(long length)
+static void pcmbuf_flush_buffer(const char *buf, size_t length)
+{
+ size_t copy_n;
+ audiobuffer_free -= length;
+ while (length > 0) {
+ size_t audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
+ if (NEED_FLUSH(audiobuffer_index))
+ {
+ pcmbuf_flush_fillpos();
+ audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
+ }
+ copy_n = MIN(length, pcmbuf_size - audiobuffer_index);
+ memcpy(&audiobuffer[audiobuffer_index], buf, copy_n);
+ buf += copy_n;
+ audiobuffer_fillpos += copy_n;
+ length -= copy_n;
+ }
+}
+
+static void flush_crossfade(const char *buf, size_t length) {
+ size_t copy_n;
+
+ while (length > 0 && crossfade_active) {
+ copy_n = MIN(length, pcmbuf_size - crossfade_pos);
+ copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
+ (const short *)buf, copy_n/2);
+ buf += copy_n;
+ length -= copy_n;
+ crossfade_pos += copy_n;
+ if (crossfade_pos >= pcmbuf_size)
+ crossfade_pos = 0;
+ }
+
+ pcmbuf_flush_buffer(buf, length);
+}
+
+static bool prepare_insert(size_t length)
{
if (crossfade_init)
crossfade_start();
@@ -611,48 +719,66 @@ static bool prepare_insert(long length)
return false;
}
- if (audiobuffer_free < length + audiobuffer_fillpos
- + CHUNK_SIZE && !crossfade_active) {
+ if (audiobuffer_free < length && !crossfade_active)
+ {
pcmbuf_boost(false);
return false;
}
- if (!pcm_is_playing()) {
+ if (!pcm_is_playing())
+ {
pcmbuf_boost(true);
crossfade_active = false;
/* Pre-buffer 1s. */
- if (audiobuffer_free < pcmbuf_size - NATIVE_FREQUENCY*4) {
+ if (!LOW_DATA(4))
+ {
logf("pcm starting");
pcmbuf_play_start();
}
}
-
return true;
}
-void* pcmbuf_request_buffer(long length, long *realsize)
+void* pcmbuf_request_buffer(size_t length, size_t *realsize)
{
- void *ptr = NULL;
-
- if (!prepare_insert(length))
- {
- *realsize = 0;
- return NULL;
- }
-
if (crossfade_active) {
- *realsize = MIN(length, PCMBUF_GUARD);
- ptr = &guardbuf[0];
- } else {
- *realsize = MIN(length, pcmbuf_size - audiobuffer_pos
- - audiobuffer_fillpos);
- if (*realsize < length) {
- *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD);
+ *realsize = MIN(length, PCMBUF_FADE_CHUNK);
+ return &guardbuf[0];
+ }
+ else
+ {
+ if(prepare_insert(length))
+ {
+ size_t audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
+ if (pcmbuf_size - audiobuffer_index < PCMBUF_MIN_CHUNK) {
+ pcmbuf_flush_fillpos();
+ audiobuffer_pos = 0;
+ *realsize = MIN(length, pcmbuf_size);
+ return &audiobuffer[0];
+ }
+ else
+ {
+ *realsize = MIN(length, pcmbuf_size - audiobuffer_index);
+ return &audiobuffer[audiobuffer_index];
+ }
+ }
+ else
+ {
+ *realsize = 0;
+ return NULL;
}
- ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
}
-
- return ptr;
+}
+
+void* pcmbuf_request_voice_buffer(size_t length, size_t *realsize, bool mix)
+{
+ if (mix)
+ {
+ *realsize = MIN(length, PCMBUF_FADE_CHUNK);
+ return &guardbuf[0];
+ }
+ else
+ return pcmbuf_request_buffer(length, realsize);
}
bool pcmbuf_is_crossfade_active(void)
@@ -660,104 +786,41 @@ bool pcmbuf_is_crossfade_active(void)
return crossfade_active || crossfade_init;
}
-void pcmbuf_flush_buffer(long length)
+void pcmbuf_write_complete(size_t length)
{
- int copy_n;
- char *buf;
-
if (crossfade_active) {
- buf = &guardbuf[0];
- length = MIN(length, PCMBUF_GUARD);
- while (length > 0 && crossfade_active) {
- copy_n = MIN(length, pcmbuf_size - crossfade_pos);
- copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
- (const short *)buf, copy_n/2);
- buf += copy_n;
- length -= copy_n;
- crossfade_pos += copy_n;
- if (crossfade_pos >= pcmbuf_size)
- crossfade_pos -= pcmbuf_size;
- }
-
- while (length > 0) {
- pcmbuf_flush_fillpos();
- copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
- memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
- audiobuffer_fillpos = copy_n;
- buf += copy_n;
- length -= copy_n;
- }
+ length = MIN(length, PCMBUF_FADE_CHUNK);
+ flush_crossfade(&guardbuf[0],length);
}
+ else
+ {
+ audiobuffer_free -= length;
+ audiobuffer_fillpos += length;
- audiobuffer_fillpos += length;
-
- try_flush:
- if (audiobuffer_fillpos < CHUNK_SIZE && pcmbuf_size
- - audiobuffer_pos - audiobuffer_fillpos > 0)
- return ;
-
- copy_n = audiobuffer_fillpos - (pcmbuf_size - audiobuffer_pos);
- if (copy_n > 0) {
- audiobuffer_fillpos -= copy_n;
- pcmbuf_flush_fillpos();
- copy_n = MIN(copy_n, PCMBUF_GUARD);
- memcpy(&audiobuffer[0], &guardbuf[0], copy_n);
- audiobuffer_fillpos = copy_n;
- goto try_flush;
+ if (NEED_FLUSH(audiobuffer_pos + audiobuffer_fillpos))
+ pcmbuf_flush_fillpos();
}
- pcmbuf_flush_fillpos();
}
-bool pcmbuf_insert_buffer(char *buf, long length)
+void pcmbuf_write_voice(size_t length)
+{
+ while (pcm_is_playing())
+ sleep(1);
+ pcm_play_data(NULL, &guardbuf[0], length);
+}
+
+bool pcmbuf_insert_buffer(const char *buf, size_t length)
{
- long copy_n = 0;
-
if (!prepare_insert(length))
return false;
-
if (crossfade_active) {
- while (length > 0 && crossfade_active) {
- copy_n = MIN(length, pcmbuf_size - crossfade_pos);
-
- copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
- (const short *)buf, copy_n/2);
- buf += copy_n;
- length -= copy_n;
- crossfade_pos += copy_n;
- if (crossfade_pos >= pcmbuf_size)
- crossfade_pos -= pcmbuf_size;
- }
-
- while (length > 0) {
- pcmbuf_flush_fillpos();
- copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
- memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
- audiobuffer_fillpos = copy_n;
- buf += copy_n;
- length -= copy_n;
- }
+ flush_crossfade(buf,length);
}
-
- while (length > 0) {
- copy_n = MIN(length, pcmbuf_size - audiobuffer_pos -
- audiobuffer_fillpos);
- copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n);
-
- memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos],
- buf, copy_n);
- buf += copy_n;
- audiobuffer_fillpos += copy_n;
- length -= copy_n;
-
- /* Pre-buffer to meet CHUNK_SIZE requirement */
- if (audiobuffer_fillpos < CHUNK_SIZE && length == 0) {
- return true;
- }
-
- pcmbuf_flush_fillpos();
+ else
+ {
+ pcmbuf_flush_buffer(buf, length);
}
-
return true;
}
@@ -765,19 +828,21 @@ bool pcmbuf_insert_buffer(char *buf, long length)
in Hertz for a duration in milliseconds. */
void pcmbuf_beep(int frequency, int duration, int amplitude)
{
- int state = 0, count = 0;
- int interval = NATIVE_FREQUENCY / frequency;
- int pos;
+ unsigned int state = 0, count = 0;
+ unsigned int interval = NATIVE_FREQUENCY / frequency;
+ size_t pos;
short *buf = (short *)audiobuffer;
- int bufsize = pcmbuf_size / 2;
+ size_t bufsize = pcmbuf_size / 2;
/* FIXME: Should start playback. */
//if (pcmbuf_unplayed_bytes * 1000 < 4 * NATIVE_FREQUENCY * duration)
// return ;
- pos = (audiobuffer_pos - pcmbuf_unplayed_bytes) / 2;
- if (pos < 0)
- pos += bufsize;
+ if (audiobuffer_pos < pcmbuf_unplayed_bytes)
+ pos = pcmbuf_size + audiobuffer_pos - pcmbuf_unplayed_bytes;
+ else
+ pos = audiobuffer_pos - pcmbuf_unplayed_bytes;
+ pos /= 2;
duration = NATIVE_FREQUENCY / 1000 * duration;
while (duration-- > 0)
@@ -821,21 +886,19 @@ int pcmbuf_mix_usage(void)
void pcmbuf_reset_mixpos(void)
{
- int bufsize = pcmbuf_size / 2;
-
pcmbuf_mix_used_bytes = 0;
- mixpos = (audiobuffer_pos - pcmbuf_unplayed_bytes) / 2;
- if (mixpos < 0)
- mixpos += bufsize;
- if (mixpos >= bufsize)
- mixpos -= bufsize;
+ if (audiobuffer_pos < pcmbuf_unplayed_bytes)
+ mixpos = pcmbuf_size + audiobuffer_pos - pcmbuf_unplayed_bytes;
+ else
+ mixpos = audiobuffer_pos - pcmbuf_unplayed_bytes;
+ mixpos /= 2;
}
-void pcmbuf_mix(char *buf, long length)
+void pcmbuf_mix(char *buf, size_t length)
{
short *ibuf = (short *)buf;
short *obuf = (short *)audiobuffer;
- int bufsize = pcmbuf_size / 2;
+ size_t bufsize = pcmbuf_size / 2;
if (pcmbuf_mix_used_bytes == 0)
pcmbuf_reset_mixpos();
@@ -858,8 +921,10 @@ void pcmbuf_crossfade_enable(bool on_off)
crossfade_enabled = on_off;
if (crossfade_enabled) {
- pcmbuf_set_watermark_bytes(pcmbuf_size - (NATIVE_FREQUENCY*4/2));
+ /* If crossfading, try to keep the buffer full other than 2 second */
+ pcmbuf_set_watermark_bytes(pcmbuf_size - PCMBUF_WATERMARK * 2);
} else {
+ /* Otherwise, just keep it above 1 second */
pcmbuf_set_watermark_bytes(PCMBUF_WATERMARK);
}
}