First audio codec playback attempt by Miikka Pekkarinen
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6574 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
b1e1e44041
commit
1c497e6045
20 changed files with 2266 additions and 153 deletions
|
@ -48,4 +48,6 @@ recorder/radio.c
|
|||
#ifdef HAVE_RECORDING
|
||||
recorder/recording.c
|
||||
#endif
|
||||
|
||||
#ifdef IRIVER_H100
|
||||
playback.c
|
||||
#endif
|
||||
|
|
1176
apps/playback.c
Normal file
1176
apps/playback.c
Normal file
File diff suppressed because it is too large
Load diff
67
apps/playback.h
Normal file
67
apps/playback.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2005 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 _AUDIO_H
|
||||
#define _AUDIO_H
|
||||
|
||||
#define AFMT_MPA_L1 0x0001 // MPEG Audio layer 1
|
||||
#define AFMT_MPA_L2 0x0002 // MPEG Audio layer 2
|
||||
#define AFMT_MPA_L3 0x0004 // MPEG Audio layer 3
|
||||
/* (MPEG-1, 2, 2.5 layers 1, 2 and 3 */
|
||||
#define AFMT_PCM_WAV 0x0008 // Uncompressed PCM in a WAV file
|
||||
#define AFMT_OGG_VORBIS 0x0010 // Ogg Vorbis
|
||||
#define AFMT_FLAC 0x0020 // FLAC
|
||||
#define AFMT_MPC 0x0040 // Musepack
|
||||
#define AFMT_AAC 0x0080 // AAC
|
||||
#define AFMT_APE 0x0100 // Monkey's Audio
|
||||
#define AFMT_WMA 0x0200 // Windows Media Audio
|
||||
#define AFMT_A52 0x0400 // A/52 (aka AC3) audio
|
||||
#define AFMT_REAL 0x0800 // Realaudio
|
||||
#define AFMT_UNKNOWN 0x1000 // Unknown file format
|
||||
|
||||
struct codec_api {
|
||||
off_t filesize;
|
||||
off_t curpos;
|
||||
size_t bitspersampe;
|
||||
|
||||
/* For gapless mp3 */
|
||||
struct mp3entry *id3;
|
||||
struct mp3info *mp3data;
|
||||
bool *taginfo_ready;
|
||||
|
||||
bool stop_codec;
|
||||
bool reload_codec;
|
||||
int seek_time;
|
||||
|
||||
void* (*get_codec_memory)(size_t *size);
|
||||
bool (*audiobuffer_insert)(char *data, size_t length);
|
||||
void (*set_elapsed)(unsigned int value);
|
||||
|
||||
size_t (*read_filebuf)(void *ptr, size_t size);
|
||||
void* (*request_buffer)(size_t *realsize, size_t reqsize);
|
||||
void (*advance_buffer)(size_t amount);
|
||||
void (*advance_buffer_loc)(void *ptr);
|
||||
bool (*seek_buffer)(off_t newpos);
|
||||
off_t (*mp3_get_filepos)(int newtime);
|
||||
bool (*request_next_track)(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -327,6 +327,65 @@ static const struct plugin_api rockbox_api = {
|
|||
strcasestr,
|
||||
};
|
||||
|
||||
#ifdef IRIVER_H100
|
||||
int codec_load_ram(char* pluginptr, size_t size, void *parameter, void* ptr2, size_t bufwrap)
|
||||
{
|
||||
enum plugin_status (*plugin_start)(struct plugin_api* api, void* param);
|
||||
int copy_n;
|
||||
|
||||
if ((int)&pluginbuf != (int)pluginptr) {
|
||||
/* zero out plugin buffer to ensure a properly zeroed bss area */
|
||||
memset(pluginbuf, 0, PLUGIN_BUFFER_SIZE);
|
||||
|
||||
size = MIN(size, PLUGIN_BUFFER_SIZE);
|
||||
copy_n = MIN(size, bufwrap);
|
||||
memcpy(pluginbuf, pluginptr, copy_n);
|
||||
|
||||
size -= copy_n;
|
||||
if (size > 0) {
|
||||
memcpy(ptr2, &pluginptr[copy_n], size);
|
||||
}
|
||||
}
|
||||
|
||||
plugin_start = (void*)&pluginbuf;
|
||||
|
||||
if (plugin_size <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
invalidate_icache();
|
||||
|
||||
return plugin_start((struct plugin_api*) &rockbox_api, parameter);
|
||||
}
|
||||
|
||||
int codec_load_file(const char *plugin, void *parameter)
|
||||
{
|
||||
char msgbuf[80];
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
fd = open(plugin, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", plugin);
|
||||
splash(HZ*2, true, msgbuf);
|
||||
return fd;
|
||||
}
|
||||
|
||||
plugin_size = 0;
|
||||
|
||||
do {
|
||||
rc = read(fd, &pluginbuf[0], PLUGIN_BUFFER_SIZE);
|
||||
if (rc < 0)
|
||||
return PLUGIN_ERROR;
|
||||
plugin_size += rc;
|
||||
} while (rc > 0) ;
|
||||
close(fd);
|
||||
|
||||
return codec_load_ram(pluginbuf, plugin_size, parameter, NULL, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int plugin_load(const char* plugin, void* parameter)
|
||||
{
|
||||
enum plugin_status (*plugin_start)(struct plugin_api* api, void* param);
|
||||
|
|
|
@ -383,6 +383,11 @@ struct plugin_api {
|
|||
};
|
||||
|
||||
/* defined by the plugin loader (plugin.c) */
|
||||
#ifdef IRIVER_H100
|
||||
int codec_load_ram(char* pluginptr, size_t size, void *parameter, void* ptr2, size_t bufwrap);
|
||||
int codec_load_file(const char* plugin, void* parameter);
|
||||
#endif
|
||||
|
||||
int plugin_load(const char* plugin, void* parameter);
|
||||
void* plugin_get_buffer(int *buffer_size);
|
||||
void* plugin_get_audio_buffer(int *buffer_size);
|
||||
|
|
|
@ -72,6 +72,10 @@ mpa2wav.c
|
|||
a52towav.c
|
||||
flac2wav.c
|
||||
vorbis2wav.c
|
||||
#ifdef IRIVER_H100
|
||||
codecvorbis.c
|
||||
codecmpa.c
|
||||
#endif
|
||||
wv2wav.c
|
||||
mpc2wav.c
|
||||
midi2wav.c
|
||||
|
|
404
apps/plugins/codecmpa.c
Normal file
404
apps/plugins/codecmpa.c
Normal file
|
@ -0,0 +1,404 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2005 Dave Chapman
|
||||
*
|
||||
* 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 "plugin.h"
|
||||
|
||||
#include <codecs/libmad/mad.h>
|
||||
|
||||
#include "playback.h"
|
||||
#include "mp3data.h"
|
||||
#include "lib/codeclib.h"
|
||||
|
||||
static struct plugin_api* rb;
|
||||
|
||||
struct mad_stream Stream IDATA_ATTR;
|
||||
struct mad_frame Frame IDATA_ATTR;
|
||||
struct mad_synth Synth IDATA_ATTR;
|
||||
mad_timer_t Timer;
|
||||
struct dither d0, d1;
|
||||
|
||||
/* The following function is used inside libmad - let's hope it's never
|
||||
called.
|
||||
*/
|
||||
|
||||
void abort(void) {
|
||||
}
|
||||
|
||||
/* The "dither" code to convert the 24-bit samples produced by libmad was
|
||||
taken from the coolplayer project - coolplayer.sourceforge.net */
|
||||
|
||||
struct dither {
|
||||
mad_fixed_t error[3];
|
||||
mad_fixed_t random;
|
||||
};
|
||||
|
||||
# define SAMPLE_DEPTH 16
|
||||
# define scale(x, y) dither((x), (y))
|
||||
|
||||
/*
|
||||
* NAME: prng()
|
||||
* DESCRIPTION: 32-bit pseudo-random number generator
|
||||
*/
|
||||
static __inline
|
||||
unsigned long prng(unsigned long state)
|
||||
{
|
||||
return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: dither()
|
||||
* DESCRIPTION: dither and scale sample
|
||||
*/
|
||||
static __inline
|
||||
signed int dither(mad_fixed_t sample, struct dither *dither)
|
||||
{
|
||||
unsigned int scalebits;
|
||||
mad_fixed_t output, mask, random;
|
||||
|
||||
enum {
|
||||
MIN = -MAD_F_ONE,
|
||||
MAX = MAD_F_ONE - 1
|
||||
};
|
||||
|
||||
/* noise shape */
|
||||
sample += dither->error[0] - dither->error[1] + dither->error[2];
|
||||
|
||||
dither->error[2] = dither->error[1];
|
||||
dither->error[1] = dither->error[0] / 2;
|
||||
|
||||
/* bias */
|
||||
output = sample + (1L << (MAD_F_FRACBITS + 1 - SAMPLE_DEPTH - 1));
|
||||
|
||||
scalebits = MAD_F_FRACBITS + 1 - SAMPLE_DEPTH;
|
||||
mask = (1L << scalebits) - 1;
|
||||
|
||||
/* dither */
|
||||
random = prng(dither->random);
|
||||
output += (random & mask) - (dither->random & mask);
|
||||
|
||||
//dither->random = random;
|
||||
|
||||
/* clip */
|
||||
if (output > MAX) {
|
||||
output = MAX;
|
||||
|
||||
if (sample > MAX)
|
||||
sample = MAX;
|
||||
}
|
||||
else if (output < MIN) {
|
||||
output = MIN;
|
||||
|
||||
if (sample < MIN)
|
||||
sample = MIN;
|
||||
}
|
||||
|
||||
/* quantize */
|
||||
output &= ~mask;
|
||||
|
||||
/* error feedback */
|
||||
dither->error[0] = sample - output;
|
||||
|
||||
/* scale */
|
||||
return output >> scalebits;
|
||||
}
|
||||
|
||||
static __inline
|
||||
signed int detect_silence(mad_fixed_t sample)
|
||||
{
|
||||
unsigned int scalebits;
|
||||
mad_fixed_t output, mask;
|
||||
|
||||
enum {
|
||||
MIN = -MAD_F_ONE,
|
||||
MAX = MAD_F_ONE - 1
|
||||
};
|
||||
|
||||
/* bias */
|
||||
output = sample + (1L << (MAD_F_FRACBITS + 1 - SAMPLE_DEPTH - 1));
|
||||
|
||||
scalebits = MAD_F_FRACBITS + 1 - SAMPLE_DEPTH;
|
||||
mask = (1L << scalebits) - 1;
|
||||
|
||||
/* clip */
|
||||
if (output > MAX) {
|
||||
output = MAX;
|
||||
|
||||
if (sample > MAX)
|
||||
sample = MAX;
|
||||
}
|
||||
else if (output < MIN) {
|
||||
output = MIN;
|
||||
|
||||
if (sample < MIN)
|
||||
sample = MIN;
|
||||
}
|
||||
|
||||
/* quantize */
|
||||
output &= ~mask;
|
||||
|
||||
/* scale */
|
||||
output >>= scalebits + 4;
|
||||
|
||||
if (output == 0x00 || output == 0xff)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#define SHRT_MAX 32767
|
||||
|
||||
#define INPUT_CHUNK_SIZE 8192
|
||||
#define OUTPUT_BUFFER_SIZE 65536 /* Must be an integer multiple of 4. */
|
||||
|
||||
unsigned char OutputBuffer[OUTPUT_BUFFER_SIZE];
|
||||
unsigned char *OutputPtr;
|
||||
unsigned char *GuardPtr=NULL;
|
||||
const unsigned char *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE;
|
||||
|
||||
mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR;
|
||||
unsigned char mad_main_data[MAD_BUFFER_MDLEN] IDATA_ATTR;
|
||||
|
||||
#ifdef USE_IRAM
|
||||
extern char iramcopy[];
|
||||
extern char iramstart[];
|
||||
extern char iramend[];
|
||||
#endif
|
||||
|
||||
#undef DEBUG_GAPLESS
|
||||
|
||||
/* this is the plugin entry point */
|
||||
enum plugin_status plugin_start(struct plugin_api* api, void* parm)
|
||||
{
|
||||
struct codec_api *ci = (struct codec_api *)parm;
|
||||
int Status=0;
|
||||
size_t size;
|
||||
int file_end;
|
||||
unsigned short Sample;
|
||||
char *InputBuffer;
|
||||
unsigned int samplecount;
|
||||
unsigned int samplesdone;
|
||||
bool first_frame;
|
||||
#ifdef DEBUG_GAPLESS
|
||||
bool first = true;
|
||||
int fd;
|
||||
#endif
|
||||
int i;
|
||||
|
||||
/* Generic plugin inititialisation */
|
||||
|
||||
TEST_PLUGIN_API(api);
|
||||
rb = api;
|
||||
|
||||
#ifdef USE_IRAM
|
||||
rb->memcpy(iramstart, iramcopy, iramend-iramstart);
|
||||
#endif
|
||||
|
||||
/* This function sets up the buffers and reads the file into RAM */
|
||||
|
||||
if (codec_init(api, ci)) {
|
||||
return PLUGIN_ERROR;
|
||||
}
|
||||
|
||||
/* Create a decoder instance */
|
||||
|
||||
next_track:
|
||||
|
||||
memset(&Stream, 0, sizeof(struct mad_stream));
|
||||
memset(&Frame, 0, sizeof(struct mad_frame));
|
||||
memset(&Synth, 0, sizeof(struct mad_synth));
|
||||
memset(&Timer, 0, sizeof(mad_timer_t));
|
||||
|
||||
mad_stream_init(&Stream);
|
||||
mad_frame_init(&Frame);
|
||||
mad_synth_init(&Synth);
|
||||
mad_timer_reset(&Timer);
|
||||
|
||||
/* We do this so libmad doesn't try to call codec_calloc() */
|
||||
memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap));
|
||||
Frame.overlap = &mad_frame_overlap;
|
||||
Stream.main_data = &mad_main_data;
|
||||
|
||||
#ifdef DEBUG_GAPLESS
|
||||
if (first)
|
||||
fd = rb->open("/first.pcm", O_WRONLY | O_CREAT);
|
||||
else
|
||||
fd = rb->open("/second.pcm", O_WRONLY | O_CREAT);
|
||||
first = false;
|
||||
#endif
|
||||
|
||||
samplesdone = 0;
|
||||
first_frame = false;
|
||||
file_end = 0;
|
||||
OutputPtr = OutputBuffer;
|
||||
|
||||
while (!*ci->taginfo_ready)
|
||||
rb->yield();
|
||||
|
||||
|
||||
ci->request_buffer(&size, ci->id3->first_frame_offset);
|
||||
ci->advance_buffer(size);
|
||||
|
||||
samplecount = ci->id3->length * (ci->id3->frequency / 100) / 10;
|
||||
/* rb->snprintf(buf2, sizeof(buf2), "sc: %d", samplecount);
|
||||
rb->splash(0, true, buf2);
|
||||
rb->snprintf(buf2, sizeof(buf2), "length: %d", ci->id3->length);
|
||||
rb->splash(HZ*5, true, buf2);
|
||||
rb->snprintf(buf2, sizeof(buf2), "frequency: %d", ci->id3->frequency);
|
||||
rb->splash(HZ*5, true, buf2); */
|
||||
|
||||
/* This is the decoding loop. */
|
||||
while (1) {
|
||||
rb->yield();
|
||||
if (ci->stop_codec || ci->reload_codec) {
|
||||
break ;
|
||||
}
|
||||
|
||||
if (ci->seek_time) {
|
||||
unsigned int sample_loc;
|
||||
int newpos;
|
||||
|
||||
sample_loc = ci->seek_time/1000 * ci->id3->frequency;
|
||||
newpos = ci->mp3_get_filepos(ci->seek_time-1);
|
||||
if (ci->seek_buffer(newpos)) {
|
||||
ci->seek_time = 0;
|
||||
if (sample_loc >= samplecount + samplesdone)
|
||||
break ;
|
||||
samplecount += samplesdone - sample_loc;
|
||||
samplesdone = sample_loc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Lock buffers */
|
||||
if (Stream.error == 0) {
|
||||
InputBuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE);
|
||||
if (size == 0 || InputBuffer == NULL)
|
||||
break ;
|
||||
mad_stream_buffer(&Stream, InputBuffer, size);
|
||||
}
|
||||
|
||||
//if ((int)ci->curpos >= ci->id3->first_frame_offset)
|
||||
//first_frame = true;
|
||||
|
||||
if(mad_frame_decode(&Frame,&Stream))
|
||||
{
|
||||
if (Stream.error == MAD_FLAG_INCOMPLETE || Stream.error == MAD_ERROR_BUFLEN) {
|
||||
// rb->splash(HZ*1, true, "Incomplete");
|
||||
/* This makes the codec to support partially corrupted files too. */
|
||||
if (file_end == 30)
|
||||
break ;
|
||||
|
||||
/* Fill the buffer */
|
||||
Stream.error = 0;
|
||||
file_end++;
|
||||
continue ;
|
||||
}
|
||||
else if(MAD_RECOVERABLE(Stream.error))
|
||||
{
|
||||
if(Stream.error!=MAD_ERROR_LOSTSYNC || Stream.this_frame!=GuardPtr)
|
||||
{
|
||||
// rb->splash(HZ*1, true, "Recoverable...!");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if(Stream.error==MAD_ERROR_BUFLEN) {
|
||||
//rb->splash(HZ*1, true, "Buflen error");
|
||||
break ;
|
||||
} else {
|
||||
//rb->splash(HZ*1, true, "Unrecoverable error");
|
||||
Status=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Stream.next_frame)
|
||||
ci->advance_buffer_loc((void *)Stream.next_frame);
|
||||
file_end = false;
|
||||
/* ?? Do we need the timer module? */
|
||||
// mad_timer_add(&Timer,Frame.header.duration);
|
||||
|
||||
/* DAVE: This can be used to attenuate the audio */
|
||||
// if(DoFilter)
|
||||
// ApplyFilter(&Frame);
|
||||
|
||||
mad_synth_frame(&Synth,&Frame);
|
||||
|
||||
//if (!first_frame) {
|
||||
//samplecount -= Synth.pcm.length;
|
||||
//continue ;
|
||||
//}
|
||||
|
||||
/* Convert MAD's numbers to an array of 16-bit LE signed integers */
|
||||
for(i=0;i<Synth.pcm.length;i++)
|
||||
{
|
||||
samplesdone++;
|
||||
//if (ci->mp3data->padding > 0) {
|
||||
// ci->mp3data->padding--;
|
||||
// continue ;
|
||||
//}
|
||||
if (!first_frame) {
|
||||
if (detect_silence(Synth.pcm.samples[0][i]))
|
||||
continue ;
|
||||
first_frame = true;
|
||||
}
|
||||
|
||||
/* Left channel */
|
||||
Sample=scale(Synth.pcm.samples[0][i],&d0);
|
||||
*(OutputPtr++)=Sample>>8;
|
||||
*(OutputPtr++)=Sample&0xff;
|
||||
|
||||
/* Right channel. If the decoded stream is monophonic then
|
||||
* the right output channel is the same as the left one.
|
||||
*/
|
||||
if(MAD_NCHANNELS(&Frame.header)==2)
|
||||
Sample=scale(Synth.pcm.samples[1][i],&d1);
|
||||
*(OutputPtr++)=Sample>>8;
|
||||
*(OutputPtr++)=Sample&0xff;
|
||||
|
||||
samplecount--;
|
||||
if (samplecount == 0) {
|
||||
#ifdef DEBUG_GAPLESS
|
||||
rb->write(fd, OutputBuffer, (int)OutputPtr-(int)OutputBuffer);
|
||||
#endif
|
||||
while (!ci->audiobuffer_insert(OutputBuffer, (int)OutputPtr-(int)OutputBuffer))
|
||||
rb->yield();
|
||||
goto song_end;
|
||||
}
|
||||
|
||||
/* Flush the buffer if it is full. */
|
||||
if(OutputPtr==OutputBufferEnd)
|
||||
{
|
||||
#ifdef DEBUG_GAPLESS
|
||||
rb->write(fd, OutputBuffer, OUTPUT_BUFFER_SIZE);
|
||||
#endif
|
||||
while (!ci->audiobuffer_insert(OutputBuffer, OUTPUT_BUFFER_SIZE))
|
||||
rb->yield();
|
||||
OutputPtr=OutputBuffer;
|
||||
}
|
||||
}
|
||||
ci->set_elapsed(samplesdone / (ci->id3->frequency/1000));
|
||||
}
|
||||
|
||||
song_end:
|
||||
#ifdef DEBUG_GAPLESS
|
||||
rb->close(fd);
|
||||
#endif
|
||||
Stream.error = 0;
|
||||
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
return PLUGIN_OK;
|
||||
}
|
160
apps/plugins/codecvorbis.c
Normal file
160
apps/plugins/codecvorbis.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2002 Björn Stenberg
|
||||
*
|
||||
* 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 "kernel.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include <codecs/Tremor/ivorbisfile.h>
|
||||
|
||||
#include "playback.h"
|
||||
#include "lib/codeclib.h"
|
||||
|
||||
static struct plugin_api* rb;
|
||||
|
||||
/* Some standard functions and variables needed by Tremor */
|
||||
|
||||
|
||||
int errno;
|
||||
|
||||
size_t strlen(const char *s) {
|
||||
return(rb->strlen(s));
|
||||
}
|
||||
|
||||
char *strcpy(char *dest, const char *src) {
|
||||
return(rb->strcpy(dest,src));
|
||||
}
|
||||
|
||||
char *strcat(char *dest, const char *src) {
|
||||
return(rb->strcat(dest,src));
|
||||
}
|
||||
|
||||
size_t read_handler(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||
struct codec_api *p = (struct codec_api *) datasource;
|
||||
|
||||
return p->read_filebuf(ptr, nmemb*size);
|
||||
}
|
||||
|
||||
int seek_handler(void *datasource, ogg_int64_t offset, int whence) {
|
||||
/* We are not seekable at the moment */
|
||||
(void)datasource;
|
||||
(void)offset;
|
||||
(void)whence;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int close_handler(void *datasource) {
|
||||
(void)datasource;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long tell_handler(void *datasource) {
|
||||
struct codec_api *p = (struct codec_api *) datasource;
|
||||
return p->curpos;
|
||||
}
|
||||
|
||||
#ifdef USE_IRAM
|
||||
extern char iramcopy[];
|
||||
extern char iramstart[];
|
||||
extern char iramend[];
|
||||
#endif
|
||||
|
||||
|
||||
/* reserve the PCM buffer in the IRAM area */
|
||||
static char pcmbuf[4096] IDATA_ATTR;
|
||||
|
||||
/* this is the plugin entry point */
|
||||
enum plugin_status plugin_start(struct plugin_api* api, void* parm)
|
||||
{
|
||||
struct codec_api *ci = (struct codec_api *)parm;
|
||||
ov_callbacks callbacks;
|
||||
OggVorbis_File vf;
|
||||
vorbis_info* vi;
|
||||
|
||||
int error;
|
||||
long n;
|
||||
int current_section;
|
||||
int eof;
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
int i;
|
||||
char x;
|
||||
#endif
|
||||
|
||||
TEST_PLUGIN_API(api);
|
||||
|
||||
/* if you are using a global api pointer, don't forget to copy it!
|
||||
otherwise you will get lovely "I04: IllInstr" errors... :-) */
|
||||
rb = api;
|
||||
|
||||
#ifdef USE_IRAM
|
||||
rb->memcpy(iramstart, iramcopy, iramend-iramstart);
|
||||
#endif
|
||||
|
||||
/* This function sets up the buffers and reads the file into RAM */
|
||||
|
||||
if (codec_init(api, ci)) {
|
||||
return PLUGIN_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* Create a decoder instance */
|
||||
|
||||
callbacks.read_func=read_handler;
|
||||
callbacks.seek_func=seek_handler;
|
||||
callbacks.tell_func=tell_handler;
|
||||
callbacks.close_func=close_handler;
|
||||
|
||||
next_track:
|
||||
error=ov_open_callbacks(ci,&vf,NULL,0,callbacks);
|
||||
|
||||
vi=ov_info(&vf,-1);
|
||||
|
||||
if (vi==NULL) {
|
||||
// rb->splash(HZ*2, true, "Vorbis Error");
|
||||
return PLUGIN_ERROR;
|
||||
}
|
||||
|
||||
eof=0;
|
||||
while (!eof) {
|
||||
/* Read host-endian signed 16 bit PCM samples */
|
||||
n=ov_read(&vf,pcmbuf,sizeof(pcmbuf),¤t_section);
|
||||
|
||||
if (n==0) {
|
||||
eof=1;
|
||||
} else if (n < 0) {
|
||||
DEBUGF("Error decoding frame\n");
|
||||
} else {
|
||||
rb->yield();
|
||||
if (ci->stop_codec || ci->reload_codec)
|
||||
break ;
|
||||
while (!ci->audiobuffer_insert(pcmbuf, n))
|
||||
rb->yield();
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
for (i=0;i<n;i+=2) {
|
||||
x=pcmbuf[i]; pcmbuf[i]=pcmbuf[i+1]; pcmbuf[i+1]=x;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (ci->request_next_track())
|
||||
goto next_track;
|
||||
|
||||
return PLUGIN_OK;
|
||||
}
|
||||
|
|
@ -35,4 +35,7 @@ playergfx.c
|
|||
#endif
|
||||
#if CONFIG_HWCODEC == MASNONE /* software codec platforms */
|
||||
xxx2wav.c
|
||||
#ifdef IRIVER_H100
|
||||
codeclib.c
|
||||
#endif
|
||||
#endif
|
||||
|
|
36
apps/plugins/lib/codeclib.c
Normal file
36
apps/plugins/lib/codeclib.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2005 Dave Chapman
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* Various "helper functions" common to all the xxx2wav decoder plugins */
|
||||
|
||||
#include "plugin.h"
|
||||
#include "playback.h"
|
||||
#include "codeclib.h"
|
||||
|
||||
struct plugin_api* local_rb;
|
||||
|
||||
int codec_init(struct plugin_api* rb, struct codec_api* ci) {
|
||||
local_rb = rb;
|
||||
|
||||
xxx2wav_set_api(rb);
|
||||
mem_ptr = 0;
|
||||
mallocbuf = (unsigned char *)ci->get_codec_memory((size_t *)&bufsize);
|
||||
|
||||
return 0;
|
||||
}
|
46
apps/plugins/lib/codeclib.h
Normal file
46
apps/plugins/lib/codeclib.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2005 Dave Chapman
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* Various "helper functions" common to all the xxx2wav decoder plugins */
|
||||
|
||||
#if CONFIG_CPU == MCF5249 && !defined(SIMULATOR)
|
||||
#define ICODE_ATTR __attribute__ ((section(".icode")))
|
||||
#define IDATA_ATTR __attribute__ ((section(".idata")))
|
||||
#define USE_IRAM 1
|
||||
#else
|
||||
#define ICODE_ATTR
|
||||
#define IDATA_ATTR
|
||||
#endif
|
||||
|
||||
extern int mem_ptr;
|
||||
extern int bufsize;
|
||||
extern unsigned char* mallocbuf; // 512K from the start of MP3 buffer
|
||||
|
||||
void* codec_malloc(size_t size);
|
||||
void* codec_calloc(size_t nmemb, size_t size);
|
||||
void* codec_alloca(size_t size);
|
||||
void* codec_realloc(void* ptr, size_t size);
|
||||
void codec_free(void* ptr);
|
||||
void *memcpy(void *dest, const void *src, size_t n);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
int memcmp(const void *s1, const void *s2, size_t n);
|
||||
void* memmove(const void *s1, const void *s2, size_t n);
|
||||
|
||||
int codec_init(struct plugin_api* rb, struct codec_api* ci);
|
||||
|
|
@ -40,14 +40,14 @@ void* codec_malloc(size_t size) {
|
|||
|
||||
x=&mallocbuf[mem_ptr];
|
||||
mem_ptr+=(size+3)&~3; // Keep memory 32-bit aligned (if it was already?)
|
||||
|
||||
/*
|
||||
if(TIME_AFTER(*(local_rb->current_tick), last_tick + HZ)) {
|
||||
local_rb->snprintf(s,30,"Memory used: %d",mem_ptr);
|
||||
local_rb->lcd_putsxy(0,80,s);
|
||||
|
||||
last_tick = *(local_rb->current_tick);
|
||||
local_rb->lcd_update();
|
||||
}
|
||||
}*/
|
||||
return(x);
|
||||
}
|
||||
|
||||
|
@ -162,6 +162,11 @@ static unsigned char wav_header[44]={'R','I','F','F', // 0 - ChunkID
|
|||
};
|
||||
|
||||
|
||||
void xxx2wav_set_api(struct plugin_api* rb)
|
||||
{
|
||||
local_rb = rb;
|
||||
}
|
||||
|
||||
int local_init(char* infilename, char* outfilename, file_info_struct* file_info, struct plugin_api* rb) {
|
||||
char s[32];
|
||||
int i,n,bytesleft;
|
||||
|
|
|
@ -64,3 +64,4 @@ void* memmove(const void *s1, const void *s2, size_t n);
|
|||
void display_status(file_info_struct* file_info);
|
||||
int local_init(char* infilename, char* outfilename, file_info_struct* file_info, struct plugin_api* rb);
|
||||
void close_wav(file_info_struct* file_info);
|
||||
void xxx2wav_set_api(struct plugin_api* rb);
|
||||
|
|
|
@ -75,8 +75,8 @@ static char current_track_path[MAX_PATH+1];
|
|||
#define WPS_PAUSE BUTTON_ON
|
||||
#define WPS_MENU (BUTTON_MODE | BUTTON_REL)
|
||||
#define WPS_MENU_PRE BUTTON_MODE
|
||||
#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
|
||||
#define WPS_BROWSE_PRE BUTTON_ON
|
||||
#define WPS_BROWSE (BUTTON_SELECT | BUTTON_REL)
|
||||
#define WPS_BROWSE_PRE BUTTON_SELECT
|
||||
#define WPS_EXIT BUTTON_OFF
|
||||
#define WPS_KEYLOCK (BUTTON_MODE | BUTTON_DOWN)
|
||||
#define WPS_ID3 (BUTTON_MODE | BUTTON_ON)
|
||||
|
|
|
@ -114,3 +114,4 @@ Luca Burelli
|
|||
Alessio Lenzi
|
||||
David Bryant
|
||||
Martin Arver
|
||||
Miikka Pekkarinen
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#ifndef PCM_PLAYBACK_H
|
||||
#define PCM_PLAYBACK_H
|
||||
|
||||
#define PCMBUF_SIZE (1*1024*1024)
|
||||
|
||||
void pcm_init(void);
|
||||
void pcm_set_frequency(unsigned int frequency);
|
||||
|
||||
|
@ -38,4 +40,8 @@ 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);
|
||||
unsigned int audiobuffer_get_latency(void);
|
||||
bool audiobuffer_insert(char *buf, size_t length);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1928,6 +1928,7 @@ void mpeg_id3_options(bool _v1first)
|
|||
v1first = _v1first;
|
||||
}
|
||||
|
||||
#ifndef IRIVER_H100
|
||||
struct mp3entry* audio_current_track()
|
||||
{
|
||||
#ifdef SIMULATOR
|
||||
|
@ -1961,6 +1962,7 @@ bool audio_has_changed_track(void)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_HWCODEC == MAS3587F
|
||||
void audio_init_playback(void)
|
||||
|
@ -2487,6 +2489,7 @@ void mpeg_set_recording_options(int frequency, int quality,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifndef IRIVER_H100
|
||||
void audio_play(int offset)
|
||||
{
|
||||
#ifdef SIMULATOR
|
||||
|
@ -2713,3 +2716,5 @@ void audio_init(void)
|
|||
dbg_cnt2us(0);
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
|
||||
#endif /* IRIVER_H100 */
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "debug.h"
|
||||
#include "panic.h"
|
||||
#include <kernel.h>
|
||||
#include "pcm_playback.h"
|
||||
#ifndef SIMULATOR
|
||||
#include "cpu.h"
|
||||
#include "i2c.h"
|
||||
|
@ -32,19 +31,47 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "pcm_playback.h"
|
||||
#include "lcd.h"
|
||||
#include "button.h"
|
||||
#include "file.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#include "sprintf.h"
|
||||
#include "button.h"
|
||||
#include <string.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 PCM_WATERMARK 0x10000
|
||||
|
||||
static bool pcm_playing;
|
||||
static bool pcm_paused;
|
||||
static int pcm_freq = 0x6; /* 44.1 is default */
|
||||
|
||||
static char *audiobuffer;
|
||||
static size_t audiobuffer_pos;
|
||||
static volatile size_t audiobuffer_free;
|
||||
static size_t audiobuffer_fillpos;
|
||||
static bool boost_mode;
|
||||
|
||||
static unsigned char *next_start;
|
||||
static long next_size;
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -75,6 +102,15 @@ static void dma_stop(void)
|
|||
IIS2CONFIG = 0x800;
|
||||
}
|
||||
|
||||
void pcm_boost(bool state)
|
||||
{
|
||||
static bool boost_state = false;
|
||||
|
||||
if (state != boost_state) {
|
||||
cpu_boost(state);
|
||||
boost_state = state;
|
||||
}
|
||||
}
|
||||
|
||||
/* set volume of the main channel */
|
||||
void pcm_set_volume(int volume)
|
||||
|
@ -113,154 +149,11 @@ void pcm_set_frequency(unsigned int frequency)
|
|||
/* the registered callback function to ask for more mp3 data */
|
||||
static void (*callback_for_more)(unsigned char**, long*) = NULL;
|
||||
|
||||
void pcm_play_data(const unsigned char* start, int size,
|
||||
void (*get_more)(unsigned char** start, long* size))
|
||||
{
|
||||
callback_for_more = get_more;
|
||||
|
||||
dma_start(start, size);
|
||||
}
|
||||
|
||||
void pcm_play_stop(void)
|
||||
{
|
||||
dma_stop();
|
||||
}
|
||||
|
||||
void pcm_play_pause(bool play)
|
||||
{
|
||||
if(pcm_paused && play)
|
||||
{
|
||||
/* Enable the FIFO and force one write to it */
|
||||
IIS2CONFIG = (pcm_freq << 12) | 0x300;
|
||||
DCR0 |= DMA_START;
|
||||
|
||||
pcm_paused = false;
|
||||
}
|
||||
else if(!pcm_paused && !play)
|
||||
{
|
||||
IIS2CONFIG = 0x800;
|
||||
pcm_paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool pcm_is_playing(void)
|
||||
{
|
||||
return pcm_playing;
|
||||
}
|
||||
|
||||
/* DMA0 Interrupt is called when the DMA has finished transfering a chunk */
|
||||
void DMA0(void) __attribute__ ((interrupt_handler, section(".icode")));
|
||||
void DMA0(void)
|
||||
{
|
||||
unsigned char* start;
|
||||
long size = 0;
|
||||
|
||||
int res = DSR0;
|
||||
|
||||
DSR0 = 1; /* Clear interrupt */
|
||||
|
||||
/* Stop on error */
|
||||
if(res & 0x70)
|
||||
{
|
||||
dma_stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callback_for_more)
|
||||
{
|
||||
callback_for_more(&start, &size);
|
||||
}
|
||||
|
||||
if(size)
|
||||
{
|
||||
SAR0 = (unsigned long)start; /* Source address */
|
||||
BCR0 = size; /* Bytes to transfer */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Finished playing */
|
||||
dma_stop();
|
||||
}
|
||||
}
|
||||
|
||||
IPR |= (1<<14); /* Clear pending interrupt request */
|
||||
}
|
||||
|
||||
void pcm_init(void)
|
||||
{
|
||||
pcm_playing = false;
|
||||
pcm_paused = false;
|
||||
|
||||
uda1380_init();
|
||||
|
||||
BUSMASTER_CTRL = 0x81; /* PARK[1,0]=10 + BCR24BIT */
|
||||
DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */
|
||||
DMAROUTE = (DMAROUTE & 0xffffff00) | DMA0_REQ_AUDIO_1;
|
||||
DMACONFIG = 1; /* DMA0Req = PDOR3 */
|
||||
|
||||
/* Reset the audio FIFO */
|
||||
IIS2CONFIG = 0x800;
|
||||
|
||||
/* Enable interrupt at level 7, priority 0 */
|
||||
ICR4 = (ICR4 & 0xffff00ff) | 0x00001c00;
|
||||
IMR &= ~(1<<14); /* bit 14 is DMA0 */
|
||||
|
||||
pcm_set_frequency(44100);
|
||||
}
|
||||
|
||||
|
||||
#define NUM_PCM_BUFFERS 16 /* Must be a power of 2 */
|
||||
#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
|
||||
|
||||
struct pcmbufdesc
|
||||
{
|
||||
void *addr;
|
||||
int size;
|
||||
void (*callback)(void); /* Call this when the buffer has been played */
|
||||
} pcmbuffers[NUM_PCM_BUFFERS];
|
||||
|
||||
int pcmbuf_read_index;
|
||||
int pcmbuf_write_index;
|
||||
int pcmbuf_unplayed_bytes;
|
||||
int pcmbuf_watermark;
|
||||
void (*pcmbuf_watermark_callback)(int bytes_left);
|
||||
|
||||
int pcm_play_num_used_buffers(void)
|
||||
{
|
||||
return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
|
||||
}
|
||||
|
||||
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 - 1))
|
||||
{
|
||||
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_play_init(void)
|
||||
{
|
||||
pcmbuf_read_index = 0;
|
||||
pcmbuf_write_index = 0;
|
||||
pcmbuf_unplayed_bytes = 0;
|
||||
pcm_play_set_watermark(0x10000, NULL);
|
||||
}
|
||||
|
||||
static int last_chunksize = 0;
|
||||
|
||||
static void pcm_play_callback(unsigned char** start, long* size)
|
||||
|
@ -269,6 +162,8 @@ static void pcm_play_callback(unsigned char** start, long* size)
|
|||
int sz;
|
||||
|
||||
pcmbuf_unplayed_bytes -= last_chunksize;
|
||||
audiobuffer_free += last_chunksize;
|
||||
|
||||
|
||||
if(desc->size == 0)
|
||||
{
|
||||
|
@ -299,7 +194,7 @@ static void pcm_play_callback(unsigned char** start, long* size)
|
|||
/* No more buffers */
|
||||
*size = 0;
|
||||
}
|
||||
#if 0
|
||||
#if 1
|
||||
if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
|
||||
{
|
||||
if(pcmbuf_watermark_callback)
|
||||
|
@ -310,6 +205,240 @@ static void pcm_play_callback(unsigned char** start, long* size)
|
|||
#endif
|
||||
}
|
||||
|
||||
void pcm_play_data(const unsigned char* start, int size,
|
||||
void (*get_more)(unsigned char** start, long* size))
|
||||
{
|
||||
callback_for_more = get_more;
|
||||
dma_start(start, size);
|
||||
|
||||
if (get_more == pcm_play_callback)
|
||||
get_more(&next_start, &next_size);
|
||||
}
|
||||
|
||||
void pcm_play_stop(void)
|
||||
{
|
||||
dma_stop();
|
||||
audiobuffer_pos = 0;
|
||||
audiobuffer_fillpos = 0;
|
||||
audiobuffer_free = PCMBUF_SIZE;
|
||||
pcmbuf_read_index = 0;
|
||||
pcmbuf_write_index = 0;
|
||||
pcmbuf_unplayed_bytes = 0;
|
||||
next_start = NULL;
|
||||
next_size = 0;
|
||||
pcm_boost(false);
|
||||
}
|
||||
|
||||
void pcm_play_pause(bool play)
|
||||
{
|
||||
if(pcm_paused && play && pcmbuf_unplayed_bytes)
|
||||
{
|
||||
/* Enable the FIFO and force one write to it */
|
||||
IIS2CONFIG = (pcm_freq << 12) | 0x300;
|
||||
DCR0 |= DMA_START;
|
||||
|
||||
pcm_paused = false;
|
||||
}
|
||||
else if(!pcm_paused && !play)
|
||||
{
|
||||
IIS2CONFIG = 0x800;
|
||||
pcm_paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool pcm_is_playing(void)
|
||||
{
|
||||
return pcm_playing;
|
||||
}
|
||||
|
||||
/* DMA0 Interrupt is called when the DMA has finished transfering a chunk */
|
||||
void DMA0(void) __attribute__ ((interrupt_handler, section(".icode")));
|
||||
void DMA0(void)
|
||||
{
|
||||
int res = DSR0;
|
||||
bool rockboy = callback_for_more != pcm_play_callback;
|
||||
|
||||
DSR0 = 1; /* Clear interrupt */
|
||||
|
||||
/* Stop on error */
|
||||
if(res & 0x70)
|
||||
{
|
||||
dma_stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callback_for_more && rockboy)
|
||||
callback_for_more(&next_start, &next_size);
|
||||
if(next_size)
|
||||
{
|
||||
SAR0 = (unsigned long)next_start; /* Source address */
|
||||
BCR0 = next_size; /* Bytes to transfer */
|
||||
if (callback_for_more && !rockboy)
|
||||
callback_for_more(&next_start, &next_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Finished playing */
|
||||
dma_stop();
|
||||
}
|
||||
}
|
||||
|
||||
IPR |= (1<<14); /* Clear pending interrupt request */
|
||||
}
|
||||
|
||||
void pcm_init(void)
|
||||
{
|
||||
pcm_playing = false;
|
||||
pcm_paused = false;
|
||||
|
||||
uda1380_init();
|
||||
|
||||
BUSMASTER_CTRL = 0x81; /* PARK[1,0]=10 + BCR24BIT */
|
||||
DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */
|
||||
DMAROUTE = (DMAROUTE & 0xffffff00) | DMA0_REQ_AUDIO_1;
|
||||
DMACONFIG = 1; /* DMA0Req = PDOR3 */
|
||||
|
||||
/* Reset the audio FIFO */
|
||||
IIS2CONFIG = 0x800;
|
||||
|
||||
/* Enable interrupt at level 7, priority 0 */
|
||||
ICR4 = (ICR4 & 0xffff00ff) | 0x00001c00;
|
||||
IMR &= ~(1<<14); /* bit 14 is DMA0 */
|
||||
|
||||
pcm_play_init();
|
||||
pcm_set_frequency(44100);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
(void)bytes_left;
|
||||
|
||||
/* Fill audio buffer by boosting cpu */
|
||||
pcm_boost(true);
|
||||
}
|
||||
|
||||
void pcm_set_boost_mode(bool state)
|
||||
{
|
||||
boost_mode = state;
|
||||
if (boost_mode)
|
||||
pcm_boost(true);
|
||||
}
|
||||
|
||||
void audiobuffer_add_event(void (*event_handler)(void))
|
||||
{
|
||||
while (!pcm_play_add_chunk(NULL, 0, event_handler))
|
||||
yield();
|
||||
}
|
||||
|
||||
unsigned int audiobuffer_get_latency(void)
|
||||
{
|
||||
int latency;
|
||||
|
||||
/* This has to be done better. */
|
||||
latency = (PCMBUF_SIZE - audiobuffer_free - audiobuffer_fillpos
|
||||
- CHUNK_SIZE)/4 / (44100/1000);
|
||||
if (latency < 0)
|
||||
latency = 0;
|
||||
|
||||
return latency;
|
||||
}
|
||||
|
||||
bool audiobuffer_insert(char *buf, size_t length)
|
||||
{
|
||||
size_t copy_n = 0;
|
||||
|
||||
if (audiobuffer_free < length + CHUNK_SIZE) {
|
||||
if (!boost_mode)
|
||||
pcm_boost(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pcm_is_playing()) {
|
||||
pcm_boost(true);
|
||||
if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*2)
|
||||
pcm_play_start();
|
||||
}
|
||||
|
||||
while (length > 0) {
|
||||
copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos -
|
||||
audiobuffer_fillpos);
|
||||
copy_n = MIN(CHUNK_SIZE, copy_n);
|
||||
memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos],
|
||||
buf, copy_n);
|
||||
buf += copy_n;
|
||||
audiobuffer_free -= copy_n;
|
||||
length -= copy_n;
|
||||
|
||||
/* Pre-buffer to meet CHUNK_SIZE requirement */
|
||||
if (copy_n + audiobuffer_fillpos < CHUNK_SIZE && length == 0) {
|
||||
audiobuffer_fillpos += copy_n;
|
||||
return true;
|
||||
}
|
||||
|
||||
copy_n += audiobuffer_fillpos;
|
||||
|
||||
while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos],
|
||||
copy_n, NULL)) {
|
||||
if (!boost_mode)
|
||||
pcm_boost(false);
|
||||
yield();
|
||||
}
|
||||
|
||||
audiobuffer_pos += copy_n;
|
||||
audiobuffer_fillpos = 0;
|
||||
|
||||
if (audiobuffer_pos >= PCMBUF_SIZE) {
|
||||
audiobuffer_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pcm_play_init(void)
|
||||
{
|
||||
audiobuffer = &audiobuf[(audiobufend - audiobuf) -
|
||||
PCMBUF_SIZE];
|
||||
audiobuffer_free = PCMBUF_SIZE;
|
||||
audiobuffer_pos = 0;
|
||||
audiobuffer_fillpos = 0;
|
||||
boost_mode = 0;
|
||||
pcmbuf_read_index = 0;
|
||||
pcmbuf_write_index = 0;
|
||||
pcmbuf_unplayed_bytes = 0;
|
||||
pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback);
|
||||
|
||||
/* Play a small chunk of zeroes to initialize the playback system. */
|
||||
audiobuffer_pos = 32000;
|
||||
audiobuffer_free -= audiobuffer_pos;
|
||||
memset(&audiobuffer[0], 0, audiobuffer_pos);
|
||||
pcm_play_add_chunk(&audiobuffer[0], audiobuffer_pos, NULL);
|
||||
pcm_play_start();
|
||||
}
|
||||
|
||||
void pcm_play_start(void)
|
||||
{
|
||||
struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
|
||||
|
@ -320,9 +449,9 @@ void pcm_play_start(void)
|
|||
{
|
||||
size = MIN(desc->size, 32768);
|
||||
start = desc->addr;
|
||||
pcm_play_data(start, size, pcm_play_callback);
|
||||
last_chunksize = size;
|
||||
desc->size -= size;
|
||||
desc->addr += size;
|
||||
pcm_play_data(start, size, pcm_play_callback);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -458,6 +458,8 @@ void sound_set(int setting, int value)
|
|||
#elif CONFIG_HWCODEC == MAS3507D
|
||||
current_volume = -780 + (value * 960 / 100); /* tenth of dB */
|
||||
set_prescaled_volume();
|
||||
#elif CONFIG_HWCODEC == MASNONE
|
||||
pcm_set_volume((value*167117) >> 16);
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
|
|
@ -47,7 +47,9 @@ sub buildzip {
|
|||
mkdir ".rockbox", 0777;
|
||||
mkdir ".rockbox/langs", 0777;
|
||||
mkdir ".rockbox/rocks", 0777;
|
||||
`find . -name "*.rock" -o -name "*.ovl" ! -empty | xargs --replace=foo cp foo .rockbox/rocks/`;
|
||||
mkdir ".rockbox/codecs", 0777;
|
||||
`find . -name "codec*.rock" ! -empty | xargs --replace=foo cp foo .rockbox/codecs/`;
|
||||
`find . -name "*.rock" -o -name "*.ovl" ! -empty ! -name "codec*.rock" | xargs --replace=foo cp foo .rockbox/rocks/`;
|
||||
|
||||
open VIEWERS, "$ROOT/apps/plugins/viewers.config" or
|
||||
die "can't open viewers.config";
|
||||
|
|
Loading…
Reference in a new issue