2002-05-09 23:54:47 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
*
|
|
|
|
* Copyright (C) 2002 Dave Chapman
|
|
|
|
*
|
2002-05-12 10:37:49 +00:00
|
|
|
* This file contains significant code from two other projects:
|
|
|
|
*
|
|
|
|
* 1) madldd - a sample application to use libmad
|
|
|
|
* 2) CoolPlayer - a win32 audio player that also uses libmad
|
|
|
|
*
|
2002-05-09 23:54:47 +00:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2004-07-08 10:12:39 +00:00
|
|
|
#ifdef HAVE_MPEG_PLAY
|
|
|
|
#ifdef HAVE_LIBMAD
|
2002-05-09 23:54:47 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <file.h>
|
|
|
|
#include <lcd.h>
|
|
|
|
#include <button.h>
|
|
|
|
#include "id3.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <mad.h>
|
|
|
|
|
2002-05-12 14:58:41 +00:00
|
|
|
#include "sound.h"
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2002-05-12 10:37:49 +00:00
|
|
|
/* 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))
|
|
|
|
|
2002-05-09 23:54:47 +00:00
|
|
|
struct mad_stream Stream;
|
|
|
|
struct mad_frame Frame;
|
|
|
|
struct mad_synth Synth;
|
|
|
|
mad_timer_t Timer;
|
|
|
|
|
2002-05-12 10:37:49 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2002-05-09 23:54:47 +00:00
|
|
|
#define INPUT_BUFFER_SIZE (5*8192)
|
|
|
|
#define OUTPUT_BUFFER_SIZE 8192 /* Must be an integer multiple of 4. */
|
2004-07-08 10:12:39 +00:00
|
|
|
void real_mpeg_play(char* fname)
|
2002-05-09 23:54:47 +00:00
|
|
|
{
|
2002-05-27 08:08:54 +00:00
|
|
|
unsigned char InputBuffer[INPUT_BUFFER_SIZE],
|
|
|
|
OutputBuffer[OUTPUT_BUFFER_SIZE],
|
|
|
|
*OutputPtr=OutputBuffer;
|
|
|
|
const unsigned char *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE;
|
|
|
|
int Status=0, i, fd;
|
|
|
|
unsigned long FrameCount=0;
|
|
|
|
sound_t sound;
|
2004-07-08 10:12:39 +00:00
|
|
|
struct mp3entry mp3;
|
2002-05-27 08:08:54 +00:00
|
|
|
static struct dither d0, d1;
|
2004-07-08 10:12:39 +00:00
|
|
|
int key=0;
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2004-07-27 14:10:48 +00:00
|
|
|
mp3info(&mp3, fname, false); /* FIXME: honor the v1first setting */
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
init_sound(&sound);
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
/* Configure sound device for this file - always select Stereo because
|
|
|
|
some sound cards don't support mono */
|
|
|
|
config_sound(&sound,mp3.frequency,2);
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
if ((fd=open(fname,O_RDONLY)) < 0) {
|
|
|
|
fprintf(stderr,"could not open %s\n",fname);
|
|
|
|
return;
|
2002-05-09 23:54:47 +00:00
|
|
|
}
|
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
/* First the structures used by libmad must be initialized. */
|
|
|
|
mad_stream_init(&Stream);
|
|
|
|
mad_frame_init(&Frame);
|
|
|
|
mad_synth_init(&Synth);
|
|
|
|
mad_timer_reset(&Timer);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (Stream.buffer==NULL || Stream.error==MAD_ERROR_BUFLEN) {
|
|
|
|
size_t ReadSize,Remaining;
|
|
|
|
unsigned char *ReadStart;
|
|
|
|
|
|
|
|
if(Stream.next_frame!=NULL) {
|
|
|
|
Remaining=Stream.bufend-Stream.next_frame;
|
|
|
|
memmove(InputBuffer,Stream.next_frame,Remaining);
|
|
|
|
ReadStart=InputBuffer+Remaining;
|
|
|
|
ReadSize=INPUT_BUFFER_SIZE-Remaining;
|
|
|
|
} else {
|
|
|
|
ReadSize=INPUT_BUFFER_SIZE,
|
|
|
|
ReadStart=InputBuffer,
|
|
|
|
Remaining=0;
|
|
|
|
}
|
|
|
|
|
2004-07-08 10:12:39 +00:00
|
|
|
if ((int)(ReadSize=read(fd,ReadStart,ReadSize)) < 0) {
|
2002-05-27 08:08:54 +00:00
|
|
|
fprintf(stderr,"end of input stream\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mad_stream_buffer(&Stream,InputBuffer,ReadSize+Remaining);
|
|
|
|
Stream.error=0;
|
2002-05-09 23:54:47 +00:00
|
|
|
}
|
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
if(mad_frame_decode(&Frame,&Stream)) {
|
|
|
|
if(MAD_RECOVERABLE(Stream.error)) {
|
|
|
|
fprintf(stderr,"recoverable frame level error\n");
|
|
|
|
fflush(stderr);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if(Stream.error==MAD_ERROR_BUFLEN) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr,"unrecoverable frame level error\n");
|
|
|
|
Status=1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2002-05-09 23:54:47 +00:00
|
|
|
}
|
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
FrameCount++;
|
|
|
|
mad_timer_add(&Timer,Frame.header.duration);
|
|
|
|
|
|
|
|
mad_synth_frame(&Synth,&Frame);
|
|
|
|
|
|
|
|
for(i=0;i<Synth.pcm.length;i++) {
|
|
|
|
unsigned short Sample;
|
|
|
|
|
|
|
|
/* Left channel */
|
|
|
|
Sample=scale(Synth.pcm.samples[0][i],&d0);
|
|
|
|
*(OutputPtr++)=Sample&0xff;
|
|
|
|
*(OutputPtr++)=Sample>>8;
|
|
|
|
|
|
|
|
/* 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&0xff;
|
|
|
|
*(OutputPtr++)=Sample>>8;
|
|
|
|
|
|
|
|
/* Flush the buffer if it is full. */
|
|
|
|
if (OutputPtr==OutputBufferEnd) {
|
|
|
|
if (output_sound(&sound, OutputBuffer,
|
|
|
|
OUTPUT_BUFFER_SIZE)!=OUTPUT_BUFFER_SIZE) {
|
|
|
|
fprintf(stderr,"PCM write error.\n");
|
|
|
|
Status=2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OutputPtr=OutputBuffer;
|
|
|
|
}
|
|
|
|
}
|
2004-07-08 10:12:39 +00:00
|
|
|
|
|
|
|
if ((key=button_get(0))==BUTTON_STOP)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
}while(1);
|
|
|
|
|
|
|
|
/* Mad is no longer used, the structures that were initialized must
|
2002-05-09 23:54:47 +00:00
|
|
|
* now be cleared.
|
2002-05-27 08:08:54 +00:00
|
|
|
*/
|
|
|
|
mad_synth_finish(&Synth);
|
|
|
|
mad_frame_finish(&Frame);
|
|
|
|
mad_stream_finish(&Stream);
|
|
|
|
|
|
|
|
/* If the output buffer is not empty and no error occured during
|
|
|
|
* the last write, then flush it. */
|
|
|
|
if(OutputPtr!=OutputBuffer && Status!=2)
|
|
|
|
{
|
|
|
|
size_t BufferSize=OutputPtr-OutputBuffer;
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2004-07-08 10:12:39 +00:00
|
|
|
if (output_sound(&sound, OutputPtr, BufferSize)!=(int)BufferSize)
|
2002-05-27 08:08:54 +00:00
|
|
|
{
|
|
|
|
fprintf(stderr,"PCM write error\n");
|
|
|
|
Status=2;
|
|
|
|
}
|
|
|
|
}
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
/* Accounting report if no error occured. */
|
|
|
|
if(!Status)
|
|
|
|
{
|
|
|
|
char Buffer[80];
|
|
|
|
|
|
|
|
mad_timer_string(Timer,Buffer,"%lu:%02lu.%03u",
|
|
|
|
MAD_UNITS_MINUTES,MAD_UNITS_MILLISECONDS,0);
|
|
|
|
fprintf(stderr,"%lu frames decoded (%s).\n",FrameCount,Buffer);
|
|
|
|
}
|
2002-05-09 23:54:47 +00:00
|
|
|
|
2002-05-27 08:08:54 +00:00
|
|
|
close_sound(&sound);
|
|
|
|
/* That's the end of the world (in the H. G. Wells way). */
|
|
|
|
return;
|
2002-05-09 23:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-07-08 10:12:39 +00:00
|
|
|
#endif /* HAVE_LIBMAD */
|
|
|
|
#endif /* HAVE_MPEG_PLAY */
|