summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/codecs.h7
-rw-r--r--apps/codecs/a52.c2
-rw-r--r--apps/codecs/flac.c2
-rw-r--r--apps/codecs/mpa.c10
-rw-r--r--apps/codecs/mpc.c4
-rw-r--r--apps/codecs/vorbis.c2
-rw-r--r--apps/codecs/wav.c2
-rw-r--r--apps/codecs/wavpack.c2
-rw-r--r--apps/main.c4
-rw-r--r--apps/pcmbuf.c550
-rw-r--r--apps/pcmbuf.h50
-rw-r--r--apps/playback.c161
-rw-r--r--apps/plugin.h3
-rw-r--r--apps/settings.c3
-rw-r--r--apps/settings_menu.c3
16 files changed, 663 insertions, 143 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 271c2ba48c..c1f3dfc88e 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -49,6 +49,7 @@ recorder/radio.c
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 a675245d27..b2cf9e5a1e 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -137,8 +137,8 @@ struct codec_api {
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 @@ struct codec_api {
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 663e7941ec..3dacc52ced 100644
--- a/apps/codecs/a52.c
+++ b/apps/codecs/a52.c
@@ -59,7 +59,7 @@ void output_audio(sample_t* samples,int flags) {
}
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 d7ae037d26..259686ed3f 100644
--- a/apps/codecs/flac.c
+++ b/apps/codecs/flac.c
@@ -83,7 +83,7 @@ FLAC__StreamDecoderWriteStatus flac_write_handler(const FLAC__SeekableStreamDeco
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 da469f2827..77565679a2 100644
--- a/apps/codecs/mpa.c
+++ b/apps/codecs/mpa.c
@@ -221,16 +221,16 @@ enum codec_status codec_start(struct codec_api* api)
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 e3a13c5bc1..abcb30b653 100644
--- a/apps/codecs/mpc.c
+++ b/apps/codecs/mpc.c
@@ -166,7 +166,7 @@ enum codec_status codec_start(struct codec_api *api)
/* 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 @@ enum codec_status codec_start(struct codec_api *api)
/* 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 03f1ae95b5..20fb79aa1d 100644
--- a/apps/codecs/vorbis.c
+++ b/apps/codecs/vorbis.c
@@ -262,7 +262,7 @@ enum codec_status codec_start(struct codec_api* api)
} 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 49bd12da1f..527d33d286 100644
--- a/apps/codecs/wav.c
+++ b/apps/codecs/wav.c
@@ -138,7 +138,7 @@ enum codec_status codec_start(struct codec_api* api)
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 d363cbecd6..f76ac5acd7 100644
--- a/apps/codecs/wavpack.c
+++ b/apps/codecs/wavpack.c
@@ -189,7 +189,7 @@ enum codec_status codec_start(struct codec_api* api)
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 4dee44421a..17f1540beb 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 @@ void init(void)
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 0000000000..1f08274fc4
--- /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 0000000000..2b4023fb98
--- /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 671312583b..0e20d97fb4 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 bool v1first = false;
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 @@ bool codec_audiobuffer_insert_callback(char *buf, long length)
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 @@ bool codec_audiobuffer_insert_callback(char *buf, long length)
} 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 @@ bool codec_audiobuffer_insert_callback(char *buf, long length)
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 @@ bool codec_audiobuffer_insert_split_callback(void *ch1, void *ch2,
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 @@ bool codec_audiobuffer_insert_split_callback(void *ch1, void *ch2,
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 @@ void codec_set_elapsed_callback(unsigned int value)
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 @@ void codec_set_offset_callback(unsigned int value)
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 @@ static bool rebuffer_and_seek(int newpos)
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 @@ bool codec_seek_buffer_callback(off_t newpos)
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 @@ bool codec_seek_buffer_callback(off_t newpos)
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 @@ void codec_configure_callback(int setting, void *value)
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 @@ void yield_codecs(void)
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 @@ void audio_play_start(int offset)
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 @@ void audio_play_start(int offset)
logf("Failure");
}
- pcm_set_boost_mode(false);
+ pcmbuf_set_boost_mode(false);
}
void audio_clear_track_entries(void)
@@ -1162,7 +1079,7 @@ void initialize_buffer_fill(void)
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 @@ void audio_check_buffer(void)
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 @@ void audio_update_trackinfo(void)
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 @@ void audio_update_trackinfo(void)
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 @@ static void audio_stop_playback(void)
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 @@ static void initiate_track_change(int peek_index)
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 @@ void audio_thread(void)
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 @@ void audio_play(int offset)
{
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 @@ void audio_ff_rewind(int newpos)
logf("rewind: %d", newpos);
if (playing) {
ci.seek_time = newpos+1;
- pcm_play_stop();
+ pcmbuf_play_stop();
paused = false;
}
}
@@ -1905,8 +1824,8 @@ void audio_init(void)
/* 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 7dff074d92..b081887328 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -303,8 +303,7 @@ struct plugin_api {
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 50a309d8da..940c0d8bbe 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -73,6 +73,7 @@ struct user_settings global_settings;
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 @@ void settings_apply(void)
}
#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 5927635d0e..90ad191d1c 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -64,6 +64,7 @@ void dac_line_in(bool enable);
#endif
#if CONFIG_HWCODEC == MASNONE
+#include "pcmbuf.h"
#include "pcm_playback.h"
#endif
@@ -1107,7 +1108,7 @@ static bool crossfade(void)
{
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;
}