Separated buffering stuff from pcm_playback to pcmbuf. Renamed some
function calls (audiobuffer -> pcmbuf etc.).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7131 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/SOURCES b/apps/SOURCES
index 271c2ba..c1f3dfc 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -49,6 +49,7 @@
recorder/recording.c
#endif
#if CONFIG_HWCODEC == MASNONE
+pcmbuf.c
playback.c
metadata.c
codecs.c
diff --git a/apps/codecs.h b/apps/codecs.h
index a675245..b2cf9e5 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -137,8 +137,8 @@
void* (*get_codec_memory)(long *size);
/* Insert PCM data into audio buffer for playback. Playback will start
automatically. */
- bool (*audiobuffer_insert)(char *data, long length);
- bool (*audiobuffer_insert_split)(void *ch1, void *ch2, long length);
+ bool (*pcmbuf_insert)(char *data, long length);
+ bool (*pcmbuf_insert_split)(void *ch1, void *ch2, long length);
/* Set song position in WPS (value in ms). */
void (*set_elapsed)(unsigned int value);
@@ -238,8 +238,7 @@
void (*bitswap)(unsigned char *data, int length);
#endif
#if CONFIG_HWCODEC == MASNONE
- void (*pcm_play_data)(const unsigned char *start, int size,
- void (*get_more)(unsigned char** start, long*size));
+ void (*pcm_play_data)(void (*get_more)(unsigned char** start, long*size));
void (*pcm_play_stop)(void);
void (*pcm_set_frequency)(unsigned int frequency);
bool (*pcm_is_playing)(void);
diff --git a/apps/codecs/a52.c b/apps/codecs/a52.c
index 663e794..3dacc52 100644
--- a/apps/codecs/a52.c
+++ b/apps/codecs/a52.c
@@ -59,7 +59,7 @@
}
rb->yield();
- while(!ci->audiobuffer_insert((unsigned char*)int16_samples,256*2*2))
+ while(!ci->pcmbuf_insert((unsigned char*)int16_samples,256*2*2))
rb->yield();
}
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c
index d7ae037..259686e 100644
--- a/apps/codecs/flac.c
+++ b/apps/codecs/flac.c
@@ -83,7 +83,7 @@
ci->set_elapsed(samplesdone/(ci->id3->frequency/1000));
rb->yield();
- while (!ci->audiobuffer_insert(pcmbuf, data_size))
+ while (!ci->pcmbuf_insert(pcmbuf, data_size))
rb->yield();
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c
index da469f2..7756567 100644
--- a/apps/codecs/mpa.c
+++ b/apps/codecs/mpa.c
@@ -221,16 +221,16 @@
ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED);
current_stereo_mode = STEREO_NONINTERLEAVED;
}
- ci->audiobuffer_insert_split(&Synth.pcm.samples[0][start_skip],
- &Synth.pcm.samples[1][start_skip],
- (Synth.pcm.length - start_skip) * 4);
+ ci->pcmbuf_insert_split(&Synth.pcm.samples[0][start_skip],
+ &Synth.pcm.samples[1][start_skip],
+ (Synth.pcm.length - start_skip) * 4);
} else {
if (current_stereo_mode != STEREO_MONO) {
ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
current_stereo_mode = STEREO_MONO;
}
- ci->audiobuffer_insert((char *)&Synth.pcm.samples[0][start_skip],
- (Synth.pcm.length - start_skip) * 4);
+ ci->pcmbuf_insert((char *)&Synth.pcm.samples[0][start_skip],
+ (Synth.pcm.length - start_skip) * 4);
}
start_skip = 0; /* not very elegant, and might want to keep this value */
diff --git a/apps/codecs/mpc.c b/apps/codecs/mpc.c
index e3a13c5..abcb30b 100644
--- a/apps/codecs/mpc.c
+++ b/apps/codecs/mpc.c
@@ -166,7 +166,7 @@
/* Flush the buffer if it is full. */
if (OutputPtr == OutputBufferEnd) {
ci->yield();
- while (!ci->audiobuffer_insert(OutputBuffer, OUTPUT_BUFFER_SIZE))
+ while (!ci->pcmbuf_insert(OutputBuffer, OUTPUT_BUFFER_SIZE))
ci->yield();
ci->set_elapsed(samplesdone/(frequency/1000));
OutputPtr = OutputBuffer;
@@ -178,7 +178,7 @@
/* Flush the remaining data in the output buffer */
if (OutputPtr > OutputBuffer) {
ci->yield();
- while (!ci->audiobuffer_insert(OutputBuffer, OutputPtr - OutputBuffer))
+ while (!ci->pcmbuf_insert(OutputBuffer, OutputPtr - OutputBuffer))
ci->yield();
}
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c
index 03f1ae9..20fb79a 100644
--- a/apps/codecs/vorbis.c
+++ b/apps/codecs/vorbis.c
@@ -262,7 +262,7 @@
} else if (n < 0) {
DEBUGF("Error decoding frame\n");
} else {
- while (!rb->audiobuffer_insert(pcmbuf, n)) {
+ while (!rb->pcmbuf_insert(pcmbuf, n)) {
rb->sleep(1);
if ( rb->seek_time ) {
/* Hmmm, a seek was requested. Throw out the
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c
index 49bd12d..527d33d 100644
--- a/apps/codecs/wav.c
+++ b/apps/codecs/wav.c
@@ -138,7 +138,7 @@
ci->set_elapsed(samplesdone/(ci->id3->frequency/1000));
rb->yield();
- while (!ci->audiobuffer_insert((unsigned char*)wavbuf, n))
+ while (!ci->pcmbuf_insert((unsigned char*)wavbuf, n))
rb->yield();
ci->advance_buffer(n);
diff --git a/apps/codecs/wavpack.c b/apps/codecs/wavpack.c
index d363cbe..f76ac5a 100644
--- a/apps/codecs/wavpack.c
+++ b/apps/codecs/wavpack.c
@@ -189,7 +189,7 @@
if (ci->stop_codec || ci->reload_codec)
break;
- while (!ci->audiobuffer_insert ((char *) temp_buffer, nsamples * 4))
+ while (!ci->pcmbuf_insert ((char *) temp_buffer, nsamples * 4))
rb->sleep (1);
ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
diff --git a/apps/main.c b/apps/main.c
index 4dee444..17f1540 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -62,7 +62,7 @@
#include "database.h"
#if (CONFIG_HWCODEC == MASNONE)
-#include "pcm_playback.h"
+#include "pcmbuf.h"
#endif
#if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR)
#include "pcm_record.h"
@@ -283,7 +283,7 @@
global_settings.superbass);
audio_init();
#if (CONFIG_HWCODEC == MASNONE)
- pcm_init();
+ pcmbuf_init();
sound_settings_apply();
#endif
#if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR)
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
new file mode 100644
index 0000000..1f08274
--- /dev/null
+++ b/apps/pcmbuf.c
@@ -0,0 +1,550 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 by Miika Pekkarinen
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stdio.h>
+#include "config.h"
+#include "debug.h"
+#include "panic.h"
+#include <kernel.h>
+#include "pcmbuf.h"
+#include "pcm_playback.h"
+#include "logf.h"
+#ifndef SIMULATOR
+#include "cpu.h"
+#endif
+#include "system.h"
+#include <string.h>
+#include "buffer.h"
+
+#define CHUNK_SIZE 32768
+/* Must be a power of 2 */
+#define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE)
+#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
+#define PCMBUF_WATERMARK (CHUNK_SIZE * 10)
+#define PCMBUF_CF_WATERMARK (PCMBUF_SIZE - CHUNK_SIZE*8)
+
+/* Audio buffer related settings. */
+static char *audiobuffer;
+static long audiobuffer_pos; /* Current audio buffer write index. */
+long audiobuffer_free; /* Amount of bytes left in the buffer. */
+static long audiobuffer_fillpos; /* Amount audiobuffer_pos will be increased. */
+static char *guardbuf;
+
+static void (*pcmbuf_event_handler)(void);
+
+/* 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_amount;
+static int crossfade_rem;
+
+
+static bool boost_mode;
+
+/* Crossfade modes. If CFM_CROSSFADE is selected, normal
+ * crossfader will activate. Selecting CFM_FLUSH is a special
+ * operation that only overwrites the pcm buffer without crossfading.
+ */
+enum {
+ CFM_CROSSFADE,
+ CFM_FLUSH
+};
+
+/* Structure we can use to queue pcm chunks in memory to be played
+ * by the driver code. */
+struct pcmbufdesc
+{
+ void *addr;
+ int size;
+ /* Call this when the buffer has been played */
+ void (*callback)(void);
+} pcmbuffers[NUM_PCM_BUFFERS];
+
+volatile int pcmbuf_read_index;
+volatile int pcmbuf_write_index;
+int pcmbuf_unplayed_bytes;
+int pcmbuf_watermark;
+void (*pcmbuf_watermark_event)(int bytes_left);
+static int last_chunksize;
+
+static void pcmbuf_boost(bool state)
+{
+ static bool boost_state = false;
+
+ if (crossfade_init || crossfade_active || boost_mode)
+ return ;
+
+ if (state != boost_state) {
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+ cpu_boost(state);
+#endif
+ boost_state = state;
+ }
+}
+
+int pcmbuf_num_used_buffers(void)
+{
+ return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
+}
+
+static void pcmbuf_callback(unsigned char** start, long* size)
+{
+ struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
+ int sz;
+
+ 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();
+
+ /* 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())
+ {
+ /* Play max 64K at a time */
+ //sz = MIN(desc->size, 32768);
+ sz = desc->size;
+ *start = desc->addr;
+ *size = sz;
+
+ /* Update the buffer descriptor */
+ desc->size -= sz;
+ desc->addr += sz;
+
+ last_chunksize = sz;
+ }
+ else
+ {
+ /* No more buffers */
+ *size = 0;
+ if (pcmbuf_event_handler)
+ pcmbuf_event_handler();
+ }
+
+ logf("cbfm:%d", *size);
+ if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
+ {
+ if(pcmbuf_watermark_event)
+ {
+ pcmbuf_watermark_event(pcmbuf_unplayed_bytes);
+ }
+ }
+}
+
+void pcmbuf_set_watermark(int numbytes, void (*callback)(int bytes_left))
+{
+ pcmbuf_watermark = numbytes;
+ pcmbuf_watermark_event = callback;
+}
+
+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;
+ return true;
+ }
+ else
+ return false;
+}
+
+void pcmbuf_watermark_callback(int bytes_left)
+{
+ /* Fill audio buffer by boosting cpu */
+ pcmbuf_boost(true);
+ if (bytes_left <= CHUNK_SIZE * 2)
+ crossfade_active = false;
+}
+
+void pcmbuf_set_boost_mode(bool state)
+{
+ if (state)
+ pcmbuf_boost(true);
+ boost_mode = state;
+}
+
+void pcmbuf_add_event(void (*event_handler)(void))
+{
+ pcmbuf_event_handler = event_handler;
+}
+
+unsigned int pcmbuf_get_latency(void)
+{
+ int latency;
+
+ /* This has to be done better. */
+ latency = (PCMBUF_SIZE - audiobuffer_free - CHUNK_SIZE)/4 / (44100/1000);
+ if (latency < 0)
+ latency = 0;
+
+ return latency;
+}
+
+bool pcmbuf_is_lowdata(void)
+{
+ if (!pcm_is_playing() || pcm_is_paused() || crossfade_init || crossfade_active)
+ return false;
+
+ if (pcmbuf_unplayed_bytes < PCMBUF_WATERMARK)
+ return true;
+
+ return false;
+}
+
+bool pcmbuf_crossfade_init(void)
+{
+ if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
+ || crossfade_active || crossfade_init) {
+ return false;
+ }
+ logf("pcmbuf_crossfade_init");
+ pcmbuf_boost(true);
+ crossfade_mode = CFM_CROSSFADE;
+ crossfade_init = true;
+
+ return true;
+
+}
+
+void pcmbuf_play_stop(void)
+{
+ pcm_play_stop();
+ last_chunksize = 0;
+ pcmbuf_unplayed_bytes = 0;
+ pcmbuf_read_index = 0;
+ pcmbuf_write_index = 0;
+ audiobuffer_pos = 0;
+ audiobuffer_fillpos = 0;
+ audiobuffer_free = PCMBUF_SIZE;
+ crossfade_init = false;
+ crossfade_active = false;
+
+ pcmbuf_set_boost_mode(false);
+ pcmbuf_boost(false);
+
+}
+
+void pcmbuf_init(void)
+{
+ audiobuffer = &audiobuf[(audiobufend - audiobuf) -
+ PCMBUF_SIZE - PCMBUF_GUARD];
+ guardbuf = &audiobuffer[PCMBUF_SIZE];
+ pcmbuf_event_handler = NULL;
+ pcm_init();
+ pcmbuf_play_stop();
+}
+
+/** 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)
+{
+ if (crossfade_init || crossfade_active || !pcm_is_playing()) {
+ pcmbuf_play_stop();
+ return ;
+ }
+
+ crossfade_mode = CFM_FLUSH;
+ crossfade_init = true;
+}
+
+void pcmbuf_flush_fillpos(void)
+{
+ int copy_n;
+
+ copy_n = MIN(audiobuffer_fillpos, CHUNK_SIZE);
+
+ if (copy_n) {
+ while (!pcmbuf_add_chunk(&audiobuffer[audiobuffer_pos],
+ copy_n, pcmbuf_event_handler)) {
+ pcmbuf_boost(false);
+ sleep(1);
+ /* This is a fatal error situation that should never happen. */
+ if (!pcm_is_playing()) {
+ logf("pcm_flush_fillpos error");
+ break ;
+ }
+ }
+ 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;
+ }
+}
+
+static void crossfade_start(void)
+{
+ int bytesleft = pcmbuf_unplayed_bytes;
+
+ crossfade_init = 0;
+ if (bytesleft < CHUNK_SIZE * 3) {
+ logf("crossfade rejected");
+ pcmbuf_play_stop();
+ return ;
+ }
+
+ logf("crossfade_start");
+ pcmbuf_flush_fillpos();
+ pcmbuf_boost(true);
+ crossfade_active = true;
+ crossfade_pos = audiobuffer_pos;
+
+ switch (crossfade_mode) {
+ case CFM_CROSSFADE:
+ crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
+ crossfade_rem = crossfade_amount;
+ break ;
+
+ case CFM_FLUSH:
+ crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
+ crossfade_rem = crossfade_amount;
+ break ;
+ }
+
+ crossfade_pos -= crossfade_amount*2;
+ if (crossfade_pos < 0)
+ crossfade_pos += PCMBUF_SIZE;
+}
+
+static __inline
+int crossfade(short *buf, const short *buf2, int length)
+{
+ int size, i;
+ int val1, val2;
+
+ size = MIN(length, crossfade_rem);
+ switch (crossfade_mode) {
+ case CFM_CROSSFADE:
+ val1 = (crossfade_rem<<10)/crossfade_amount;
+ val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount;
+
+ for (i = 0; i < size; i++) {
+ buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10;
+ }
+ break ;
+
+ case CFM_FLUSH:
+ for (i = 0; i < size; i++) {
+ buf[i] = buf2[i];
+ }
+ //memcpy((char *)buf, (char *)buf2, size*2);
+ break ;
+ }
+
+ crossfade_rem -= size;
+ if (crossfade_rem <= 0)
+ crossfade_active = false;
+
+ return size;
+}
+
+static bool prepare_insert(long length)
+{
+ if (crossfade_init)
+ crossfade_start();
+
+ if (audiobuffer_free < length + audiobuffer_fillpos
+ + CHUNK_SIZE && !crossfade_active) {
+ pcmbuf_boost(false);
+ return false;
+ }
+
+ if (!pcm_is_playing()) {
+ pcmbuf_boost(true);
+ crossfade_active = false;
+ if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*4) {
+ logf("pcm starting");
+ pcm_play_data(pcmbuf_callback);
+ }
+ }
+
+ return true;
+}
+
+void* pcmbuf_request_buffer(long length, long *realsize)
+{
+ void *ptr = NULL;
+
+ while (audiobuffer_free < length + audiobuffer_fillpos
+ + CHUNK_SIZE && !crossfade_active) {
+ pcmbuf_boost(false);
+ sleep(1);
+ }
+
+ 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);
+ }
+ ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
+ }
+
+ return ptr;
+}
+
+bool pcmbuf_is_crossfade_active(void)
+{
+ return crossfade_active || crossfade_init;
+}
+
+void pcmbuf_flush_buffer(long length)
+{
+ int copy_n;
+ char *buf;
+
+ prepare_insert(length);
+
+ 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) {
+ 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;
+ if (length > 0)
+ pcmbuf_flush_fillpos();
+ }
+ }
+
+ 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;
+ }
+ pcmbuf_flush_fillpos();
+}
+
+bool pcmbuf_insert_buffer(char *buf, long 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) {
+ 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;
+ if (length > 0)
+ pcmbuf_flush_fillpos();
+ }
+ }
+
+ 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();
+ }
+
+ return true;
+}
+
+void pcmbuf_crossfade_enable(bool on_off)
+{
+ crossfade_enabled = on_off;
+
+ if (crossfade_enabled) {
+ pcmbuf_set_watermark(PCMBUF_CF_WATERMARK, pcmbuf_watermark_callback);
+ } else {
+ pcmbuf_set_watermark(PCMBUF_WATERMARK, pcmbuf_watermark_callback);
+ }
+}
+
+bool pcmbuf_is_crossfade_enabled(void)
+{
+ return crossfade_enabled;
+}
+
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
new file mode 100644
index 0000000..2b4023f
--- /dev/null
+++ b/apps/pcmbuf.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 by Miika Pekkarinen
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef PCMBUF_H
+#define PCMBUF_H
+
+/* Guard buffer for crossfader when dsp is enabled. */
+#define PCMBUF_GUARD 32768
+
+/* PCM audio buffer. */
+#define PCMBUF_SIZE (1*1024*1024)
+
+void pcmbuf_init(void);
+
+void pcmbuf_play_stop(void);
+bool pcmbuf_is_crossfade_active(void);
+
+/* These functions are for playing chained buffers of PCM data */
+bool pcmbuf_add_chunk(void *addr, int size, void (*callback)(void));
+int pcmbuf_num_used_buffers(void);
+void pcmbuf_set_watermark(int numbytes, void (*callback)(int bytes_left));
+
+void pcmbuf_set_boost_mode(bool state);
+bool pcmbuf_is_lowdata(void);
+void pcmbuf_flush_audio(void);
+bool pcmbuf_crossfade_init(void);
+void pcmbuf_add_event(void (*event_handler)(void));
+unsigned int pcmbuf_get_latency(void);
+bool pcmbuf_insert_buffer(char *buf, long length);
+void pcmbuf_flush_buffer(long length);
+void* pcmbuf_request_buffer(long length, long *realsize);
+bool pcmbuf_is_crossfade_enabled(void);
+void pcmbuf_crossfade_enable(bool on_off);
+
+#endif
diff --git a/apps/playback.c b/apps/playback.c
index 6713125..0e20d97 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -46,6 +46,7 @@
#include "screens.h"
#include "playlist.h"
#include "playback.h"
+#include "pcmbuf.h"
#include "pcm_playback.h"
#include "buffer.h"
#include "dsp.h"
@@ -173,91 +174,7 @@
static void mp3_set_elapsed(struct mp3entry* id3);
int mp3_get_file_pos(void);
-/* Simulator stubs. */
-#ifdef SIMULATOR
-bool pcm_insert_buffer(char *buf, long length)
-{
- (void)buf;
- (void)length;
-
- return true;
-}
-
-void pcm_flush_buffer(long length)
-{
- (void)length;
-}
-
-
-void* pcm_request_buffer(long length, long *realsize)
-{
- static char temp_audiobuffer[32768];
-
- *realsize = MIN((int)sizeof(temp_audiobuffer), length);
-
- return temp_audiobuffer;
-}
-
-void audiobuffer_add_event(void (*event_handler)(void))
-{
- event_handler();
-}
-
-unsigned int audiobuffer_get_latency()
-{
- return 0;
-}
-
-void pcm_play_stop(void)
-{
-}
-
-bool pcm_is_playing(void)
-{
- return false;
-}
-
-bool pcm_is_crossfade_active(void)
-{
- return false;
-}
-
-bool pcm_is_lowdata(void)
-{
- return false;
-}
-
-void pcm_flush_audio(void)
-{
-}
-
-bool pcm_crossfade_init(void)
-{
- return false;
-}
-
-void pcm_set_boost_mode(bool state)
-{
- (void)state;
-}
-
-bool pcm_is_crossfade_enabled(void)
-{
- return false;
-}
-
-void pcm_play_pause(bool state)
-{
- (void)state;
-}
-
-int ata_sleep(void)
-{
- return 0;
-}
-#endif
-
-bool codec_audiobuffer_insert_callback(char *buf, long length)
+bool codec_pcmbuf_insert_callback(char *buf, long length)
{
char *dest;
long realsize;
@@ -284,11 +201,11 @@
while (length > 0) {
/* Request a few extra bytes for resampling. */
/* FIXME: Required extra bytes SHOULD be calculated. */
- while ((dest = pcm_request_buffer(length+16384, &realsize)) == NULL)
+ while ((dest = pcmbuf_request_buffer(length+16384, &realsize)) == NULL)
yield();
if (realsize < 16384) {
- pcm_flush_buffer(0);
+ pcmbuf_flush_buffer(0);
continue ;
}
@@ -300,7 +217,7 @@
} else {
processed_length = dsp_process(dest, buf, realsize >> (mono + 1));
}
- pcm_flush_buffer(processed_length);
+ pcmbuf_flush_buffer(processed_length);
length -= realsize;
buf += realsize << (factor + mono);
}
@@ -308,7 +225,7 @@
return true;
}
-bool codec_audiobuffer_insert_split_callback(void *ch1, void *ch2,
+bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
long length)
{
char *dest;
@@ -326,11 +243,11 @@
while (length > 0) {
/* Request a few extra bytes for resampling. */
- while ((dest = pcm_request_buffer(length+4096, &realsize)) == NULL)
+ while ((dest = pcmbuf_request_buffer(length+4096, &realsize)) == NULL)
yield();
if (realsize < 4096) {
- pcm_flush_buffer(0);
+ pcmbuf_flush_buffer(0);
continue ;
}
@@ -338,7 +255,7 @@
processed_length = dsp_process(dest, ch1, realsize / 4) * 2;
dsp_process(dest, ch2, realsize / 4);
- pcm_flush_buffer(processed_length);
+ pcmbuf_flush_buffer(processed_length);
length -= realsize;
ch1 += realsize >> factor;
ch2 += realsize >> factor;
@@ -360,7 +277,7 @@
if (ci.stop_codec)
return ;
- latency = audiobuffer_get_latency();
+ latency = pcmbuf_get_latency();
if (value < latency) {
cur_ti->id3.elapsed = 0;
@@ -377,10 +294,10 @@
if (ci.stop_codec)
return ;
- /* The 1000 here is a hack. audiobuffer_get_latency() should
+ /* The 1000 here is a hack. pcmbuf_get_latency() should
* be more accurate
*/
- latency = (audiobuffer_get_latency() + 1000) * cur_ti->id3.bitrate / 8;
+ latency = (pcmbuf_get_latency() + 1000) * cur_ti->id3.bitrate / 8;
if (value < latency) {
cur_ti->id3.offset = 0;
@@ -487,7 +404,7 @@
ci.curpos = newpos;
cur_ti->available = 0;
lseek(current_fd, newpos, SEEK_SET);
- pcm_flush_audio();
+ pcmbuf_flush_audio();
mutex_unlock(&mutex_bufferfill);
@@ -553,8 +470,8 @@
if (difference >= 0) {
logf("seek: +%d", difference);
codec_advance_buffer_callback(difference);
- if (!pcm_is_crossfade_active())
- pcm_play_stop();
+ if (!pcmbuf_is_crossfade_active())
+ pcmbuf_play_stop();
return true;
}
@@ -575,8 +492,8 @@
if (buf_ridx < 0)
buf_ridx = codecbuflen + buf_ridx;
ci.curpos -= difference;
- if (!pcm_is_crossfade_active())
- pcm_play_stop();
+ if (!pcmbuf_is_crossfade_active())
+ pcmbuf_play_stop();
return true;
}
@@ -598,9 +515,9 @@
case CODEC_DSP_ENABLE:
if ((bool)value)
- ci.audiobuffer_insert = codec_audiobuffer_insert_callback;
+ ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
else
- ci.audiobuffer_insert = pcm_insert_buffer;
+ ci.pcmbuf_insert = pcmbuf_insert_buffer;
break ;
default:
@@ -640,7 +557,7 @@
yield();
if (!pcm_is_playing())
sleep(5);
- while ((pcm_is_crossfade_active() || pcm_is_lowdata())
+ while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata())
&& !ci.stop_codec && playing && queue_empty(&audio_queue)
&& codecbufused > (128*1024))
yield();
@@ -1067,7 +984,7 @@
buf_ridx = 0;
buf_widx = 0;
codecbufused = 0;
- pcm_set_boost_mode(true);
+ pcmbuf_set_boost_mode(true);
fill_bytesleft = codecbuflen;
filling = true;
@@ -1082,7 +999,7 @@
logf("Failure");
}
- pcm_set_boost_mode(false);
+ pcmbuf_set_boost_mode(false);
}
void audio_clear_track_entries(void)
@@ -1162,7 +1079,7 @@
fill_bytesleft = codecbuflen - codecbufused;
cur_ti->start_pos = ci.curpos;
- pcm_set_boost_mode(true);
+ pcmbuf_set_boost_mode(true);
if (filling)
return ;
@@ -1222,10 +1139,12 @@
generate_postbuffer_events();
filling = false;
conf_bufferlimit = 0;
- pcm_set_boost_mode(false);
-
+ pcmbuf_set_boost_mode(false);
+
+#ifndef SIMULATOR
if (playing)
ata_sleep();
+#endif
}
}
@@ -1242,7 +1161,7 @@
buf_ridx -= codecbuflen;
if (!filling)
- pcm_set_boost_mode(false);
+ pcmbuf_set_boost_mode(false);
} else {
buf_ridx -= ci.curpos + cur_ti->codecsize;
codecbufused += ci.curpos + cur_ti->codecsize;
@@ -1263,8 +1182,8 @@
ci.curpos = 0;
cur_ti->start_pos = 0;
ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready;
- if (!pcm_crossfade_init())
- audiobuffer_add_event(codec_track_changed);
+ if (!pcmbuf_crossfade_init())
+ pcmbuf_add_event(codec_track_changed);
else
codec_track_changed();
}
@@ -1278,7 +1197,7 @@
close(current_fd);
current_fd = -1;
}
- pcm_play_stop();
+ pcmbuf_play_stop();
pcm_play_pause(true);
track_count = 0;
audio_clear_track_entries();
@@ -1448,8 +1367,8 @@
queue_post(&audio_queue, AUDIO_PLAY, 0);
}
- else if (!pcm_crossfade_init())
- pcm_flush_audio();
+ else if (!pcmbuf_crossfade_init())
+ pcmbuf_flush_audio();
codec_track_changed();
}
@@ -1473,8 +1392,8 @@
ci.stop_codec = true;
ci.reload_codec = false;
ci.seek_time = 0;
- if (!pcm_crossfade_init() && !pcm_is_crossfade_active())
- pcm_flush_audio();
+ if (!pcmbuf_crossfade_init() && !pcmbuf_is_crossfade_active())
+ pcmbuf_flush_audio();
audio_play_start((int)ev.data);
playlist_update_resume_info(audio_current_track());
break ;
@@ -1650,8 +1569,8 @@
{
logf("audio_play");
ci.stop_codec = true;
- if (!pcm_crossfade_init())
- pcm_flush_audio();
+ if (!pcmbuf_crossfade_init())
+ pcmbuf_flush_audio();
codec_track_changed();
pcm_play_pause(true);
@@ -1696,7 +1615,7 @@
logf("rewind: %d", newpos);
if (playing) {
ci.seek_time = newpos+1;
- pcm_play_stop();
+ pcmbuf_play_stop();
paused = false;
}
}
@@ -1905,8 +1824,8 @@
/* Initialize codec api. */
ci.read_filebuf = codec_filebuf_callback;
- ci.audiobuffer_insert = pcm_insert_buffer;
- ci.audiobuffer_insert_split = codec_audiobuffer_insert_split_callback;
+ ci.pcmbuf_insert = pcmbuf_insert_buffer;
+ ci.pcmbuf_insert_split = codec_pcmbuf_insert_split_callback;
ci.get_codec_memory = get_codec_memory_callback;
ci.request_buffer = codec_request_buffer_callback;
ci.advance_buffer = codec_advance_buffer_callback;
diff --git a/apps/plugin.h b/apps/plugin.h
index 7dff074..b081887 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -303,8 +303,7 @@
void (*bitswap)(unsigned char *data, int length);
#endif
#if CONFIG_HWCODEC == MASNONE
- void (*pcm_play_data)(const unsigned char *start, int size,
- void (*get_more)(unsigned char** start, long*size));
+ void (*pcm_play_data)(void (*get_more)(unsigned char** start, long*size));
void (*pcm_play_stop)(void);
void (*pcm_set_frequency)(unsigned int frequency);
bool (*pcm_is_playing)(void);
diff --git a/apps/settings.c b/apps/settings.c
index 50a309d..940c0d8 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -73,6 +73,7 @@
const char rec_base_directory[] = REC_BASE_DIR;
#endif
#if CONFIG_HWCODEC == MASNONE
+#include "pcmbuf.h"
#include "pcm_playback.h"
#endif
@@ -845,7 +846,7 @@
}
#if CONFIG_HWCODEC == MASNONE && !defined(SIMULATOR)
- pcm_crossfade_enable(global_settings.crossfade);
+ pcmbuf_crossfade_enable(global_settings.crossfade);
#endif
#ifdef HAVE_SPDIF_POWER
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index 5927635..90ad191 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -64,6 +64,7 @@
#endif
#if CONFIG_HWCODEC == MASNONE
+#include "pcmbuf.h"
#include "pcm_playback.h"
#endif
@@ -1107,7 +1108,7 @@
{
bool rc = set_bool( str(LANG_CROSSFADE), &global_settings.crossfade );
#ifndef SIMULATOR
- pcm_crossfade_enable(global_settings.crossfade);
+ pcmbuf_crossfade_enable(global_settings.crossfade);
#endif
return rc;
}
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h
index c29cd2a..3f0b5ee 100644
--- a/firmware/export/pcm_playback.h
+++ b/firmware/export/pcm_playback.h
@@ -19,41 +19,15 @@
#ifndef PCM_PLAYBACK_H
#define PCM_PLAYBACK_H
-/* Guard buffer for crossfader when dsp is enabled. */
-#define PCMBUF_GUARD 32768
-
-/* PCM audio buffer. */
-#define PCMBUF_SIZE (1*1024*1024)
-
void pcm_init(void);
void pcm_set_frequency(unsigned int frequency);
/* This is for playing "raw" PCM data */
-void pcm_play_data(const unsigned char* start, int size,
- void (*get_more)(unsigned char** start, long* size));
+void pcm_play_data(void (*get_more)(unsigned char** start, long* size));
void pcm_play_stop(void);
void pcm_play_pause(bool play);
+bool pcm_is_paused(void);
bool pcm_is_playing(void);
-bool pcm_is_crossfade_active(void);
-
-/* These functions are for playing chained buffers of PCM data */
-void pcm_play_init(void);
-void pcm_play_start(void);
-bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void));
-int pcm_play_num_used_buffers(void);
-void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left));
-
-void pcm_set_boost_mode(bool state);
-bool pcm_is_lowdata(void);
-void pcm_flush_audio(void);
-bool pcm_crossfade_init(void);
-void audiobuffer_add_event(void (*event_handler)(void));
-unsigned int audiobuffer_get_latency(void);
-bool pcm_insert_buffer(char *buf, long length);
-void pcm_flush_buffer(long length);
-void* pcm_request_buffer(long length, long *realsize);
-bool pcm_is_crossfade_enabled(void);
-void pcm_crossfade_enable(bool on_off);
#endif
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c
index af8ce18..7682ce0 100644
--- a/firmware/pcm_playback.c
+++ b/firmware/pcm_playback.c
@@ -43,61 +43,13 @@
#ifdef HAVE_UDA1380
-#define CHUNK_SIZE 32768
-/* Must be a power of 2 */
-#define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE)
-#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
-#define PCM_WATERMARK (CHUNK_SIZE * 10)
-#define PCM_CF_WATERMARK (PCMBUF_SIZE - CHUNK_SIZE*8)
-
static bool pcm_playing;
static bool pcm_paused;
static int pcm_freq = 0x6; /* 44.1 is default */
-static char *audiobuffer;
-static long audiobuffer_pos;
-long audiobuffer_free;
-static long audiobuffer_fillpos;
-static bool boost_mode;
-
-/* Crossfade modes. If CFM_CROSSFADE is selected, normal
- * crossfader will activate. Selecting CFM_FLUSH is a special
- * operation that only overwrites the pcm buffer without crossfading.
- */
-enum {
- CFM_CROSSFADE,
- CFM_FLUSH
-};
-
-static int crossfade_mode;
-static bool crossfade_enabled;
-static bool crossfade_active;
-static bool crossfade_init;
-static int crossfade_pos;
-static int crossfade_amount;
-static int crossfade_rem;
-
-static char *guardbuf;
-static void (*pcm_event_handler)(void);
-
static unsigned char *next_start;
static long next_size;
-static int last_chunksize = 0;
-
-struct pcmbufdesc
-{
- void *addr;
- int size;
- void (*callback)(void); /* Call this when the buffer has been played */
-} pcmbuffers[NUM_PCM_BUFFERS];
-
-volatile int pcmbuf_read_index;
-volatile int pcmbuf_write_index;
-int pcmbuf_unplayed_bytes;
-int pcmbuf_watermark;
-void (*pcmbuf_watermark_callback)(int bytes_left);
-
/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
static void dma_start(const void *addr, long size)
{
@@ -122,21 +74,6 @@
DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_SINC | DMA_START;
}
-void pcm_boost(bool state)
-{
- static bool boost_state = false;
-
- if (crossfade_init || crossfade_active || boost_mode)
- return ;
-
- if (state != boost_state) {
-#ifdef HAVE_ADJUSTABLE_CPU_FREQ
- cpu_boost(state);
-#endif
- boost_state = state;
- }
-}
-
/* Stops the DMA transfer and interrupt */
static void dma_stop(void)
{
@@ -147,17 +84,8 @@
IIS2CONFIG = 0x800;
EBU1CONFIG = 0x800;
- pcmbuf_unplayed_bytes = 0;
- last_chunksize = 0;
- audiobuffer_pos = 0;
- audiobuffer_fillpos = 0;
- audiobuffer_free = PCMBUF_SIZE;
- pcmbuf_read_index = 0;
- pcmbuf_write_index = 0;
next_start = NULL;
next_size = 0;
- crossfade_init = 0;
- crossfade_active = 0;
pcm_paused = false;
}
@@ -185,67 +113,13 @@
/* the registered callback function to ask for more mp3 data */
static void (*callback_for_more)(unsigned char**, long*) = NULL;
-int pcm_play_num_used_buffers(void)
+void pcm_play_data(void (*get_more)(unsigned char** start, long* size))
{
- return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
-}
-
-static void pcm_play_callback(unsigned char** start, long* size)
-{
- struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
- int sz;
-
- pcmbuf_unplayed_bytes -= last_chunksize;
- audiobuffer_free += last_chunksize;
+ unsigned char *start;
+ long size;
-
- if(desc->size == 0)
- {
- /* The buffer is finished, call the callback function */
- if(desc->callback)
- desc->callback();
-
- /* Advance to the next buffer */
- pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
- desc = &pcmbuffers[pcmbuf_read_index];
- }
-
- if(pcm_play_num_used_buffers())
- {
- /* Play max 64K at a time */
- //sz = MIN(desc->size, 32768);
- sz = desc->size;
- *start = desc->addr;
- *size = sz;
-
- /* Update the buffer descriptor */
- desc->size -= sz;
- desc->addr += sz;
-
- last_chunksize = sz;
- }
- else
- {
- /* No more buffers */
- *size = 0;
- if (pcm_event_handler)
- pcm_event_handler();
- }
-#if 1
- if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
- {
- if(pcmbuf_watermark_callback)
- {
- pcmbuf_watermark_callback(pcmbuf_unplayed_bytes);
- }
- }
-#endif
-}
-
-void pcm_play_data(const unsigned char* start, int size,
- void (*get_more)(unsigned char** start, long* size))
-{
callback_for_more = get_more;
+
/** FIXME: This is a temporary fix to prevent playback glitches when
* playing the first file. We will just drop the first frame to prevent
* that problem from occurring.
@@ -259,6 +133,7 @@
* find the explanation for this bug from this file.
*/
get_more((unsigned char **)&start, (long *)&size); // REMOVE THIS TO TEST
+ get_more((unsigned char **)&start, (long *)&size);
get_more(&next_start, &next_size);
dma_start(start, size);
uda1380_mute(false);
@@ -270,13 +145,11 @@
uda1380_mute(true);
dma_stop();
}
- pcm_set_boost_mode(false);
- pcm_boost(false);
}
void pcm_play_pause(bool play)
{
- if(pcm_paused && play && pcmbuf_unplayed_bytes)
+ if(pcm_paused && play && next_size)
{
logf("unpause");
/* Reset chunk size so dma has enough data to fill the fifo. */
@@ -303,6 +176,11 @@
pcm_paused = !play;
}
+bool pcm_is_paused(void)
+{
+ return pcm_paused;
+}
+
bool pcm_is_playing(void)
{
return pcm_playing;
@@ -370,390 +248,8 @@
sleep(HZ/4);
uda1380_enable_output(true);
- pcm_play_init();
-}
-
-void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left))
-{
- pcmbuf_watermark = numbytes;
- pcmbuf_watermark_callback = callback;
-}
-
-bool pcm_play_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(pcm_play_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;
- return true;
- }
- else
- return false;
-}
-
-void pcm_watermark_callback(int bytes_left)
-{
- /* Fill audio buffer by boosting cpu */
- pcm_boost(true);
- if (bytes_left <= CHUNK_SIZE * 2)
- crossfade_active = false;
-}
-
-void pcm_set_boost_mode(bool state)
-{
- if (state)
- pcm_boost(true);
- boost_mode = state;
-}
-
-void audiobuffer_add_event(void (*event_handler)(void))
-{
- pcm_event_handler = event_handler;
-}
-
-unsigned int audiobuffer_get_latency(void)
-{
- int latency;
-
- /* This has to be done better. */
- latency = (PCMBUF_SIZE - audiobuffer_free - CHUNK_SIZE)/4 / (44100/1000);
- if (latency < 0)
- latency = 0;
-
- return latency;
-}
-
-bool pcm_is_lowdata(void)
-{
- if (!pcm_is_playing() || pcm_paused || crossfade_init || crossfade_active)
- return false;
-
- if (pcmbuf_unplayed_bytes < PCM_WATERMARK)
- return true;
-
- return false;
-}
-
-bool pcm_crossfade_init(void)
-{
- if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
- || crossfade_active || crossfade_init) {
- return false;
- }
- logf("pcm_crossfade_init");
- pcm_boost(true);
- crossfade_mode = CFM_CROSSFADE;
- crossfade_init = true;
-
- return true;
-
-}
-
-/** Initialize a track switch so that audio playback will not stop but
- * the switch to next track would happen as soon as possible.
- */
-void pcm_flush_audio(void)
-{
- if (crossfade_init || crossfade_active || !pcm_playing) {
- pcm_play_stop();
- return ;
- }
-
- crossfade_mode = CFM_FLUSH;
- crossfade_init = true;
-}
-
-void pcm_flush_fillpos(void)
-{
- int copy_n;
-
- copy_n = MIN(audiobuffer_fillpos, CHUNK_SIZE);
-
- if (copy_n) {
- while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos],
- copy_n, pcm_event_handler)) {
- pcm_boost(false);
- sleep(1);
- /* This is a fatal error situation that should never happen. */
- if (!pcm_playing) {
- logf("pcm_flush_fillpos error");
- break ;
- }
- }
- pcm_event_handler = NULL;
- audiobuffer_pos += copy_n;
- if (audiobuffer_pos >= PCMBUF_SIZE)
- audiobuffer_pos -= PCMBUF_SIZE;
- audiobuffer_free -= copy_n;
- audiobuffer_fillpos -= copy_n;
- }
-}
-
-static void crossfade_start(void)
-{
- int bytesleft = pcmbuf_unplayed_bytes;
-
- crossfade_init = 0;
- if (bytesleft < CHUNK_SIZE * 3) {
- logf("crossfade rejected");
- pcm_play_stop();
- return ;
- }
-
- logf("crossfade_start");
- pcm_flush_fillpos();
- pcm_boost(true);
- crossfade_active = true;
- crossfade_pos = audiobuffer_pos;
-
- switch (crossfade_mode) {
- case CFM_CROSSFADE:
- crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
- crossfade_rem = crossfade_amount;
- break ;
-
- case CFM_FLUSH:
- crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2;
- crossfade_rem = crossfade_amount;
- break ;
- }
-
- crossfade_pos -= crossfade_amount*2;
- if (crossfade_pos < 0)
- crossfade_pos += PCMBUF_SIZE;
-}
-
-static __inline
-int crossfade(short *buf, const short *buf2, int length)
-{
- int size, i;
- int val1, val2;
-
- size = MIN(length, crossfade_rem);
- switch (crossfade_mode) {
- case CFM_CROSSFADE:
- val1 = (crossfade_rem<<10)/crossfade_amount;
- val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount;
-
- for (i = 0; i < size; i++) {
- buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10;
- }
- break ;
-
- case CFM_FLUSH:
- for (i = 0; i < size; i++) {
- buf[i] = buf2[i];
- }
- //memcpy((char *)buf, (char *)buf2, size*2);
- break ;
- }
-
- crossfade_rem -= size;
- if (crossfade_rem <= 0)
- crossfade_active = false;
-
- return size;
-}
-
-static bool prepare_insert(long length)
-{
- if (crossfade_init)
- crossfade_start();
-
- if (audiobuffer_free < length + audiobuffer_fillpos
- + CHUNK_SIZE && !crossfade_active) {
- pcm_boost(false);
- return false;
- }
-
- if (!pcm_is_playing()) {
- pcm_boost(true);
- crossfade_active = false;
- if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*4)
- pcm_play_start();
- }
-
- return true;
-}
-
-void* pcm_request_buffer(long length, long *realsize)
-{
- void *ptr = NULL;
-
- while (audiobuffer_free < length + audiobuffer_fillpos
- + CHUNK_SIZE && !crossfade_active) {
- pcm_boost(false);
- sleep(1);
- }
-
- 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);
- }
- ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
- }
-
- return ptr;
-}
-
-bool pcm_is_crossfade_active(void)
-{
- return crossfade_active || crossfade_init;
-}
-
-void pcm_flush_buffer(long length)
-{
- int copy_n;
- char *buf;
-
- prepare_insert(length);
-
- 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) {
- 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;
- if (length > 0)
- pcm_flush_fillpos();
- }
- }
-
- 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;
- pcm_flush_fillpos();
- copy_n = MIN(copy_n, PCMBUF_GUARD);
- memcpy(&audiobuffer[0], &guardbuf[0], copy_n);
- audiobuffer_fillpos = copy_n;
- goto try_flush;
- }
- pcm_flush_fillpos();
-}
-
-bool pcm_insert_buffer(char *buf, long 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) {
- 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;
- if (length > 0)
- pcm_flush_fillpos();
- }
- }
-
- 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;
- }
-
- pcm_flush_fillpos();
- }
-
- return true;
-}
-
-void pcm_play_init(void)
-{
- audiobuffer = &audiobuf[(audiobufend - audiobuf) -
- PCMBUF_SIZE - PCMBUF_GUARD];
- guardbuf = &audiobuffer[PCMBUF_SIZE];
-
/* Call dma_stop to initialize everything. */
dma_stop();
- pcm_event_handler = NULL;
-}
-
-void pcm_crossfade_enable(bool on_off)
-{
- crossfade_enabled = on_off;
-}
-
-bool pcm_is_crossfade_enabled(void)
-{
- return crossfade_enabled;
-}
-
-void pcm_play_start(void)
-{
- unsigned long size;
- unsigned char *start;
-
- if (crossfade_enabled) {
- pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback);
- } else {
- pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback);
- }
- crossfade_active = false;
-
- if(!pcm_is_playing())
- {
- pcm_play_callback(&start, &size);
- pcm_play_data(start, size, pcm_play_callback);
- }
}
#endif /* HAVE_UDA1380 */
diff --git a/uisimulator/common/stubs.c b/uisimulator/common/stubs.c
index 3b61e27..5865b2c 100644
--- a/uisimulator/common/stubs.c
+++ b/uisimulator/common/stubs.c
@@ -33,7 +33,36 @@
extern char having_new_lcd;
+/* Stubs for PCM audio playback. */
+bool pcm_is_playing(void)
+{
+ return false;
+}
+void pcm_play_pause(bool state)
+{
+ (void)state;
+}
+
+bool pcm_is_paused(void)
+{
+ return false;
+}
+
+void pcm_play_stop(void)
+{
+}
+
+void pcm_init(void)
+{
+}
+
+void pcm_play_data(void (*get_more)(unsigned char** start, long* size))
+{
+ (void)get_more;
+}
+
+/* Generic firmware stubs. */
void backlight_on(void)
{
/* we could do something better here! */