summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorFranklin Wei <frankhwei536@gmail.com>2016-11-19 13:17:31 -0500
committerFranklin Wei <frankhwei536@gmail.com>2016-11-19 13:17:31 -0500
commit830f282cf7b4408d434511af74597d8c02104dc9 (patch)
treeb2f4f8e9063af2605adcf67ec523cc30376c350e /apps/plugins
parent23115e9856bd9d13b85186b47112723ab2c87772 (diff)
parent9470ec89122ead9913f5a94128cc57b29c5d7004 (diff)
downloadrockbox-830f282cf7b4408d434511af74597d8c02104dc9.zip
rockbox-830f282cf7b4408d434511af74597d8c02104dc9.tar.gz
rockbox-830f282cf7b4408d434511af74597d8c02104dc9.tar.bz2
rockbox-830f282cf7b4408d434511af74597d8c02104dc9.tar.xz
Merge branch 'picotts' into working
Change-Id: Ie7797ea3dd9cae4ee7f83da8031fad9695109693
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/picotts/SOURCES1
-rw-r--r--apps/plugins/picotts/main.c165
-rw-r--r--apps/plugins/picotts/pcm_output.c296
-rw-r--r--apps/plugins/picotts/pcm_output.h53
4 files changed, 408 insertions, 107 deletions
diff --git a/apps/plugins/picotts/SOURCES b/apps/plugins/picotts/SOURCES
index 381a3be..44c8945 100644
--- a/apps/plugins/picotts/SOURCES
+++ b/apps/plugins/picotts/SOURCES
@@ -2,6 +2,7 @@ main.c
math.c
stdio_compat.c
strstr.c
+pcm_output.c
libsvox/picoacph.c
libsvox/picoapi.c
libsvox/picobase.c
diff --git a/apps/plugins/picotts/main.c b/apps/plugins/picotts/main.c
index 10ca0d8..d3622e1 100644
--- a/apps/plugins/picotts/main.c
+++ b/apps/plugins/picotts/main.c
@@ -19,14 +19,12 @@
**************************************************************************/
#include "plugin.h"
#include "keydefs.h"
+#include "pcm_output.h"
#include "libsvox/picoapi.h"
#include "libsvox/picodefs.h"
#define EV_EXIT 100
-#define OUTBUF_SIZE (18*1024)
-#define OUTBUF_NUM (8)
-
const char * picoSupportedCountryIso3[] = { "USA", "GBR", "DEU", "ESP", "FRA", "ITA" };
const char * picoInternalTaLingware[] = { "en-US_ta.bin", "en-GB_ta.bin", "de-DE_ta.bin", "es-ES_ta.bin", "fr-FR_ta.bin", "it-IT_ta.bin" };
const char * picoInternalSgLingware[] = { "en-US_lh0_sg.bin", "en-GB_kh0_sg.bin", "de-DE_gl0_sg.bin", "es-ES_zl0_sg.bin", "fr-FR_nk0_sg.bin", "it-IT_cm0_sg.bin" };
@@ -130,89 +128,47 @@ void close_wav(void)
rb->close(wavinfo.fd);
}
-struct buf_t {
- int bufused;
- int16_t buffer[OUTBUF_SIZE];
-};
-
-/* circulart buffer */
-struct bufmgr_t {
- struct buf_t circbuffer[OUTBUF_NUM];
- volatile unsigned char head;
- volatile unsigned char tail;
-};
-static struct bufmgr_t *bufmgr;
-
-static void *bufmgr_chunk_push(void)
-{
- void *ptr = &(bufmgr->circbuffer[(bufmgr->head & (OUTBUF_NUM-1))]);
- bufmgr->head++;
-
- return ptr;
-}
-
-static void *bufmgr_chunk_pop(void)
-{
- void *ptr = &(bufmgr->circbuffer[(bufmgr->tail & (OUTBUF_NUM-1))]);
- bufmgr->tail++;
-
- return ptr;
-}
-
-static bool bufmgr_empty(void)
-{
- return (bufmgr->head == bufmgr->tail) ? true : false;
-}
-
-static bool bufmgr_full(void)
-{
- if (bufmgr->head >= bufmgr->tail)
- return bufmgr->head - bufmgr->tail > (OUTBUF_NUM-2);
- else
- return bufmgr->head + 256 - bufmgr->tail > (OUTBUF_NUM-2);
-}
-
-#ifdef HAVE_SCHEDULER_BOOSTCTRL
-static bool bufmgr_low(void)
-{
- if (bufmgr->head >= bufmgr->tail)
- return bufmgr->head - bufmgr->tail < (OUTBUF_NUM/3);
- else
- return bufmgr->head + 256 - bufmgr->tail < (OUTBUF_NUM/3);
-}
-#endif
-
-static void bufmgr_init(void *mem)
-{
- bufmgr = (struct bufmgr_t *)mem;
- bufmgr->head = 0;
- bufmgr->tail = 0;
-}
-
static pico_Status synth(void)
{
- struct dsp_buffer src;
- struct dsp_buffer dst;
- char synthbuf[2*1024];
+ struct dsp_buffer src;
+ struct dsp_buffer dst;
+ static char ALIGNED_ATTR(4) synthbuf[2*1024];
unsigned int synthbuf_used = 0;
pico_Status ret;
pico_Int16 bytes_recv;
pico_Int16 out_data_type;
- struct buf_t *buf = bufmgr_chunk_push();
+DEBUGF("synth() src 0x%lx dst 0x%lx\n", (unsigned int)&src, (unsigned int)&dst);
void *outbuf_ptr = synthbuf;
do
{
/* get samples */
ret = pico_getData(picoEngine, (void *)outbuf_ptr, 128, &bytes_recv, &out_data_type);
+DEBUGF("pico_getData()\n");
if (bytes_recv)
{
synthbuf_used += bytes_recv;
+DEBUGF("synthbuf_used: %d\n", synthbuf_used);
outbuf_ptr += bytes_recv;
if (synthbuf_used + 128 > sizeof(synthbuf))
{
+DEBUGF("pico out buffer ready\n");
+
+ ssize_t sz = synthbuf_used*3*2;
+ void *buf = NULL;
+ while (true)
+ {
+ buf = pcm_output_get_buffer(&sz);
+ if (buf)
+ {
+DEBUGF("pcm_output_get_buffer: 0x%lx 0x%lx\n", (unsigned int)buf, sz);
+ break;
+ }
+ rb->yield();
+ DEBUGF("pcm_output_get_buffer() == NULL\n");
+ }
/* picotts output is 16bit mono PCM 16kHz samplerate */
src.remcount = synthbuf_used >> 1; /* Samples in buffer */
src.pin[0] = synthbuf; /* Data pointer(s) */
@@ -222,8 +178,8 @@ static pico_Status synth(void)
* to DSP. */
dst.remcount = 0; /* Samples in buffer */
- dst.p16out = buf->buffer; /* DSP output buffer */
- dst.bufcount = OUTBUF_SIZE >> 2; /* Buffer length/dest
+ dst.p16out = buf; /* DSP output buffer */
+ dst.bufcount = sz >> 2; /* Buffer length/dest
* buffer remaining
* (samples)
*/
@@ -232,7 +188,9 @@ static pico_Status synth(void)
do
{
old_remcount = dst.remcount;
+DEBUGF("dsp_process(%p, %p, %p) enter\n", (void *)dsp, (void *)&src, (void *)&dst);
rb->dsp_process(dsp, &src, &dst);
+DEBUGF("dsp_process() quit\n");
} while (dst.bufcount <= 0 ||
(src.remcount <= 0 &&
dst.remcount <= old_remcount));
@@ -240,31 +198,22 @@ static pico_Status synth(void)
/* number of bytes ready in output buffer
* which is passed to DMA.
* DSP engine returns 16bit stereo interleaved
- */
- buf->bufused = dst.remcount << 2;
-
+ */
+ pcm_output_commit_data(dst.remcount << 2);
+DEBUGF("pcm_output_commit_data(%d)\n", synthbuf_used);
//DEBUGF("%d: synth() %d\n", *(rb->current_tick), buf->bufused);
//rb->write(wavinfo.fd, buf->buffer, buf->bufused);
-
+ DEBUGF("synth() quit BUSY\n");
return PICO_STEP_BUSY;
}
-
-
}
} while (PICO_STEP_BUSY == ret);
+ DEBUGF("synth() quit IDLE\n");
return PICO_STEP_IDLE;
}
-static void get_more(const void** start, size_t* size)
-{
- struct buf_t *buf = bufmgr_chunk_pop();
-
- *start = buf->buffer;
- *size = buf->bufused;
-}
-
static pico_Status load_voice(int langIndex)
{
pico_Status ret;
@@ -367,14 +316,13 @@ static void change_volume(int delta)
static void tts_thread(void)
{
+
struct queue_event ev;
int text_remaining = filelen;
- pico_Char *input = (pico_Char *)mem_pool + sizeof(struct bufmgr_t);
+ pico_Char *input = (pico_Char *)mem_pool;
pico_Int16 bytes_sent;
pico_Status ret;
- bufmgr_init(mem_pool);
-
while (text_remaining)
{
/* put text into TTS engine input buffer */
@@ -388,7 +336,7 @@ static void tts_thread(void)
if (ev.id == EV_EXIT)
return;
- if (bufmgr_full())
+ if (pcm_output_bytes_used() >= PCMOUT_HIGH_WM)
{
#ifdef HAVE_SCHEDULER_BOOSTCTRL
rb->cancel_cpu_boost();
@@ -398,7 +346,7 @@ static void tts_thread(void)
else
{
#ifdef HAVE_SCHEDULER_BOOSTCTRL
- if (bufmgr_low())
+ if (pcm_output_bytes_used() <= PCMOUT_LOW_WM)
{
rb->trigger_cpu_boost();
}
@@ -406,6 +354,7 @@ static void tts_thread(void)
ret = synth();
if (ret != PICO_STEP_BUSY)
{
+DEBUGF("synth() ret=%d\n", ret);
break;
}
}
@@ -421,6 +370,7 @@ enum plugin_status plugin_start(const void* file)
/* grab audio buffer */
mem_pool = rb->plugin_get_audio_buffer(&mem_pool_size);
+DEBUGF("plugin_get_audio_buffer: %p 0x%0x\n", mem_pool, mem_pool_size);
int fd = rb->open(file, O_RDONLY);
if (fd < 0)
@@ -430,12 +380,24 @@ enum plugin_status plugin_start(const void* file)
}
filelen = rb->filesize(fd);
- rb->read(fd, mem_pool + sizeof(struct bufmgr_t), filelen);
+ rb->read(fd, mem_pool, filelen);
rb->close(fd);
+DEBUGF("File %s len=0x%x (%d)\n", file, filelen, filelen);
+
+ void *pcmbuf_pool = ALIGN_UP(((char *)mem_pool + filelen), 4);
+ size_t pcmbuf_size = PCMOUT_BUFSIZE;
+ pcm_output_init(pcmbuf_pool, &pcmbuf_size);
+
+DEBUGF("PCM out buffer %p 0x%x\n", pcmbuf_pool, pcmbuf_size);
+
+ void *pico_pool = ALIGN_UP(((char *)pcmbuf_pool + pcmbuf_size), 4);
+ size_t pico_pool_size = (char *)mem_pool + mem_pool_size - (char *)pico_pool;
+
+DEBUGF("pico_initialize %p 0x%x\n", pico_pool, pico_pool_size);
/* initialize TTS engine */
- ret = pico_initialize(mem_pool + filelen + sizeof(struct bufmgr_t),
- mem_pool_size - filelen - sizeof(struct bufmgr_t),
+ ret = pico_initialize(pico_pool,
+ pico_pool_size,
&picoSystem);
if (PICO_OK != ret)
{
@@ -460,17 +422,10 @@ enum plugin_status plugin_start(const void* file)
return PLUGIN_ERROR;
}
- /* setup pcm rockbox audio and dsp systems */
- rb->pcm_play_stop();
- rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
- rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
- rb->pcm_set_frequency(*(rb->hw_freq_sampr));
-
dsp = rb->dsp_get_config(CODEC_IDX_AUDIO);
rb->dsp_configure(dsp, DSP_RESET, 0);
rb->dsp_configure(dsp, DSP_FLUSH, 0);
rb->dsp_configure(dsp, DSP_SET_FREQUENCY, 16000);
- rb->dsp_configure(dsp, DSP_SET_OUT_FREQUENCY, *(rb->hw_freq_sampr));
rb->dsp_configure(dsp, DSP_SET_STEREO_MODE, STEREO_MONO);
rb->dsp_configure(dsp, DSP_SET_SAMPLE_DEPTH, 16);
@@ -481,16 +436,12 @@ enum plugin_status plugin_start(const void* file)
/* steal codec thread for tts synthesis */
rb->codec_thread_do_callback(tts_thread, &thread_id);
- /* wait for tts thread filling pcm buffers */
- while (!bufmgr_full())
- rb->yield();
-
- /* fire up audio DMA */
- rb->pcm_play_data(&get_more, NULL, NULL, 0);
+ /* start playback */
+ pcm_output_play_pause(true);
// init_wav("/tts.wav");
- while(!bufmgr_empty())
+ while(pcm_output_bytes_used() > PCMOUT_EMPTY_WM)
{
int button = rb->button_get_w_tmo(HZ/20);
switch(button)
@@ -502,7 +453,7 @@ enum plugin_status plugin_start(const void* file)
rb->codec_thread_do_callback(NULL, NULL);
/* drain pcm buffer */
- while(!bufmgr_empty())
+ while(pcm_output_bytes_used())
;
break;
@@ -515,7 +466,7 @@ enum plugin_status plugin_start(const void* file)
break;
case BTN_PAUSE:
- rb->pcm_play_pause(!is_playing);
+ pcm_output_play_pause(!is_playing);
is_playing = !is_playing;
break;
@@ -533,7 +484,7 @@ enum plugin_status plugin_start(const void* file)
}
}
- rb->pcm_play_stop();
+ pcm_output_stop();
// close_wav();
if (picoEngine)
diff --git a/apps/plugins/picotts/pcm_output.c b/apps/plugins/picotts/pcm_output.c
new file mode 100644
index 0000000..de6f124
--- /dev/null
+++ b/apps/plugins/picotts/pcm_output.c
@@ -0,0 +1,296 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * PCM output buffer definitions
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "plugin.h"
+#include "pcm_output.h"
+
+/* PCM channel we're using */
+#define PCM_CHANNEL PCM_MIXER_CHAN_PLAYBACK
+
+#define SILENCE_TEST_TONE 1
+/* Pointers */
+
+/* Start of buffer */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcm_buffer;
+/* End of buffer (not guard) */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_end;
+/* Read pointer */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_head IBSS_ATTR;
+/* Write pointer */
+static struct pcm_frame_header * ALIGNED_ATTR(4) pcmbuf_tail IBSS_ATTR;
+
+/* Bytes */
+static ssize_t pcmbuf_curr_size IBSS_ATTR; /* Size of currently playing frame */
+static ssize_t pcmbuf_read IBSS_ATTR; /* Number of bytes read by DMA */
+static ssize_t pcmbuf_written IBSS_ATTR; /* Number of bytes written by source */
+static ssize_t pcmbuf_threshold IBSS_ATTR; /* Non-silence threshold */
+
+static int pcm_skipped = 0;
+static int pcm_underruns = 0;
+
+/* Small silence clip. ~5.80ms @ 44.1kHz */
+static int16_t silence[256*2] ALIGNED_ATTR(4) = { 0 };
+
+/* Delete all buffer contents */
+static void pcm_reset_buffer(void)
+{
+ pcmbuf_threshold = PCMOUT_PLAY_WM;
+ pcmbuf_curr_size = pcmbuf_read = pcmbuf_written = 0;
+ pcmbuf_head = pcmbuf_tail = pcm_buffer;
+ pcm_skipped = pcm_underruns = 0;
+}
+
+/* Advance a PCM buffer pointer by size bytes circularly */
+static inline void pcm_advance_buffer(struct pcm_frame_header **p,
+ size_t size)
+{
+ *p = SKIPBYTES(*p, size);
+ if (*p >= pcmbuf_end)
+ *p = SKIPBYTES(*p, -PCMOUT_BUFSIZE);
+}
+
+/* Return physical space used */
+ssize_t pcm_output_bytes_used(void)
+{
+ return pcmbuf_written - pcmbuf_read; /* wrap-safe */
+}
+
+/* Return physical space free */
+static inline ssize_t pcm_output_bytes_free(void)
+{
+ return PCMOUT_BUFSIZE - pcm_output_bytes_used();
+}
+
+/* Audio DMA handler */
+static void get_more(const void **start, size_t *size)
+{
+ ssize_t sz;
+
+ /* Free-up the last frame played frame if any */
+ pcmbuf_read += pcmbuf_curr_size;
+ pcmbuf_curr_size = 0;
+
+ sz = pcm_output_bytes_used();
+
+ if (sz > pcmbuf_threshold)
+ {
+ pcmbuf_threshold = PCMOUT_EMPTY_WM;
+
+ while (1)
+ {
+ sz = pcmbuf_head->size;
+
+ if (sz < (ssize_t)(PCM_HDR_SIZE + 4) ||
+ (sz & 3) != 0)
+ {
+ /* Just show a warning about this - will never happen
+ * without a corrupted buffer */
+ DEBUGF("get_more: invalid size (%ld)\n", (long)sz);
+ }
+
+ /* Frame less than 100ms early - play it */
+ struct pcm_frame_header *head = pcmbuf_head;
+ pcm_advance_buffer(&pcmbuf_head, sz);
+ pcmbuf_curr_size = sz;
+
+DEBUGF("get_more() pcm_curr_size %d\n", sz);
+ sz -= PCM_HDR_SIZE;
+
+ *start = head->data;
+ *size = sz;
+ return;
+ }
+ }
+ else
+ {
+ /* Ran out so revert to default watermark */
+ if (pcmbuf_threshold == PCMOUT_EMPTY_WM)
+ pcm_underruns++;
+
+ pcmbuf_threshold = PCMOUT_PLAY_WM;
+ }
+
+ *start = silence;
+ *size = sizeof (silence);
+
+ if (sz < 0)
+ pcmbuf_read = pcmbuf_written;
+}
+
+/** Public interface **/
+
+/* Return a buffer pointer if at least size bytes are available and if so,
+ * give the actual free space */
+void * pcm_output_get_buffer(ssize_t *size)
+{
+ ssize_t sz = *size;
+ ssize_t free = pcm_output_bytes_free() - PCM_HDR_SIZE;
+
+ if (sz >= 0 && free >= sz)
+ {
+ *size = free; /* return actual free space (- header) */
+ return pcmbuf_tail->data;
+ }
+
+ /* Leave *size alone so caller doesn't have to reinit */
+ return NULL;
+}
+
+/* Commit the buffer returned by pcm_ouput_get_buffer; timestamp is PCM
+ * clock time units, not video format time units */
+bool pcm_output_commit_data(ssize_t size)
+{
+ if (size <= 0 || (size & 3))
+ return false; /* invalid */
+
+ size += PCM_HDR_SIZE;
+
+ if (size > pcm_output_bytes_free())
+ return false; /* too big */
+
+ pcmbuf_tail->size = size;
+
+ pcm_advance_buffer(&pcmbuf_tail, size);
+ pcmbuf_written += size;
+DEBUGF("pcm_output_commit_data(%d)\n", pcmbuf_written);
+ return true;
+}
+
+/* Returns 'true' if the buffer is completely empty */
+bool pcm_output_empty(void)
+{
+ return pcm_output_bytes_used() <= 0;
+}
+
+/* Flushes the buffer - clock keeps counting */
+void pcm_output_flush(void)
+{
+ rb->pcm_play_lock();
+
+ enum channel_status status = rb->mixer_channel_status(PCM_CHANNEL);
+
+ /* Stop PCM to clear current buffer */
+ if (status != CHANNEL_STOPPED)
+ rb->mixer_channel_stop(PCM_CHANNEL);
+
+ rb->pcm_play_unlock();
+
+ pcm_reset_buffer();
+
+ /* Restart if playing state was current */
+ if (status == CHANNEL_PLAYING)
+ rb->mixer_channel_play_data(PCM_CHANNEL,
+ get_more, NULL, 0);
+}
+
+/* Pauses/Starts pcm playback - and the clock */
+void pcm_output_play_pause(bool play)
+{
+ rb->pcm_play_lock();
+
+ if (rb->mixer_channel_status(PCM_CHANNEL) != CHANNEL_STOPPED)
+ {
+ rb->mixer_channel_play_pause(PCM_CHANNEL, play);
+ rb->pcm_play_unlock();
+ }
+ else
+ {
+ rb->pcm_play_unlock();
+
+ if (play)
+ {
+ rb->mixer_channel_set_amplitude(PCM_CHANNEL, MIX_AMP_UNITY);
+ rb->mixer_channel_play_data(PCM_CHANNEL,
+ get_more, NULL, 0);
+ }
+ }
+}
+
+/* Stops all playback and resets the clock */
+void pcm_output_stop(void)
+{
+ rb->pcm_play_lock();
+
+ if (rb->mixer_channel_status(PCM_CHANNEL) != CHANNEL_STOPPED)
+ rb->mixer_channel_stop(PCM_CHANNEL);
+
+ rb->pcm_play_unlock();
+
+ pcm_output_flush();
+}
+
+/* Drains any data if the start threshold hasn't been reached */
+void pcm_output_drain(void)
+{
+ rb->pcm_play_lock();
+ pcmbuf_threshold = PCMOUT_EMPTY_WM;
+ rb->pcm_play_unlock();
+}
+
+bool pcm_output_init(void *mem_ptr, size_t *size)
+{
+ /* 32bit align */
+ pcm_buffer = mem_ptr;
+ if (pcm_buffer == NULL)
+ return false;
+
+ ALIGN_BUFFER(pcm_buffer, *size, CACHEALIGN_UP(4));
+ pcmbuf_end = SKIPBYTES(pcm_buffer, *size);
+
+ pcm_reset_buffer();
+
+#if INPUT_SRC_CAPS != 0
+ /* Select playback */
+ rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
+ rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
+#endif
+
+#if SILENCE_TEST_TONE
+ /* Make the silence clip a square wave */
+ const int16_t silence_amp = INT16_MAX / 16;
+ unsigned i;
+
+ for (i = 0; i < ARRAYLEN(silence); i += 2)
+ {
+ if (i < ARRAYLEN(silence)/2)
+ {
+ silence[i] = silence_amp;
+ silence[i+1] = silence_amp;
+ }
+ else
+ {
+ silence[i] = -silence_amp;
+ silence[i+1] = -silence_amp;
+ }
+ }
+#endif
+
+ //old_sampr = rb->mixer_get_frequency();
+ //rb->mixer_set_frequency(CLOCK_RATE);
+ return true;
+}
+
+void pcm_output_exit(void)
+{
+ //if (old_sampr != 0)
+ // rb->mixer_set_frequency(old_sampr);
+}
diff --git a/apps/plugins/picotts/pcm_output.h b/apps/plugins/picotts/pcm_output.h
new file mode 100644
index 0000000..3fe9669
--- /dev/null
+++ b/apps/plugins/picotts/pcm_output.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * PCM output buffer declarations
+ *
+ * Copyright (c) 2007 Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef PCM_OUTPUT_H
+#define PCM_OUTPUT_H
+
+#ifndef ALIGNED_ATTR
+#define ALIGNED_ATTR(x) __attribute__((aligned(x)))
+#endif
+
+#define PCM_HDR_SIZE (sizeof (struct pcm_frame_header))
+#define PCMOUT_BUFSIZE ((48000/2)*4) /* 1/2 s @48kHz */
+#define PCMOUT_HIGH_WM ((PCMOUT_BUFSIZE*3)/4)
+#define PCMOUT_PLAY_WM (PCMOUT_BUFSIZE/4)
+#define PCMOUT_LOW_WM (PCMOUT_BUFSIZE/16)
+#define PCMOUT_EMPTY_WM (0)
+struct pcm_frame_header /* Header added to pcm data every time a decoded
+ audio frame is sent out */
+{
+ uint32_t size; /* size of this frame - including header */
+ unsigned char data[]; /* open array of audio data */
+} ALIGNED_ATTR(4);
+
+bool pcm_output_init(void *mem, size_t *size);
+void pcm_output_exit(void);
+void pcm_output_flush(void);
+void pcm_output_play_pause(bool play);
+void pcm_output_stop(void);
+void pcm_output_drain(void);
+void * pcm_output_get_buffer(ssize_t *size);
+bool pcm_output_commit_data(ssize_t size);
+bool pcm_output_empty(void);
+
+#endif /* PCM_OUTPUT_H */