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:
Linus Nielsen Feltzing 2005-06-05 23:05:10 +00:00
parent b1e1e44041
commit 1c497e6045
20 changed files with 2266 additions and 153 deletions

View file

@ -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

File diff suppressed because it is too large Load diff

67
apps/playback.h Normal file
View 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

View file

@ -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);

View file

@ -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);

View file

@ -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
View 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
View 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),&current_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;
}

View file

@ -35,4 +35,7 @@ playergfx.c
#endif
#if CONFIG_HWCODEC == MASNONE /* software codec platforms */
xxx2wav.c
#ifdef IRIVER_H100
codeclib.c
#endif
#endif

View 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;
}

View 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);

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -114,3 +114,4 @@ Luca Burelli
Alessio Lenzi
David Bryant
Martin Arver
Miikka Pekkarinen

View file

@ -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

View file

@ -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 */

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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";