diff options
| author | Franklin Wei <frankhwei536@gmail.com> | 2016-11-19 13:17:31 -0500 |
|---|---|---|
| committer | Franklin Wei <frankhwei536@gmail.com> | 2016-11-19 13:17:31 -0500 |
| commit | 830f282cf7b4408d434511af74597d8c02104dc9 (patch) | |
| tree | b2f4f8e9063af2605adcf67ec523cc30376c350e /apps/plugins | |
| parent | 23115e9856bd9d13b85186b47112723ab2c87772 (diff) | |
| parent | 9470ec89122ead9913f5a94128cc57b29c5d7004 (diff) | |
| download | rockbox-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/SOURCES | 1 | ||||
| -rw-r--r-- | apps/plugins/picotts/main.c | 165 | ||||
| -rw-r--r-- | apps/plugins/picotts/pcm_output.c | 296 | ||||
| -rw-r--r-- | apps/plugins/picotts/pcm_output.h | 53 |
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 */ |