mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-31 17:31:20 +02:00
our current experimental Neutrino branch
git-svn-id: file:///home/bas/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@27 e54a6e83-5905-42d5-8d5c-058d10e6a962
This commit is contained in:
22
src/driver/audiodec/Makefile.am
Normal file
22
src/driver/audiodec/Makefile.am
Normal file
@@ -0,0 +1,22 @@
|
||||
AM_CPPFLAGS = -fno-rtti -fno-exceptions
|
||||
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/src/zapit/include \
|
||||
-I$(top_srcdir)/lib/libconfigfile \
|
||||
-I$(top_srcdir)/lib/libcoolstream \
|
||||
@FREETYPE_CFLAGS@ \
|
||||
@VORBISIDEC_CFLAGS@
|
||||
|
||||
noinst_LIBRARIES = libneutrino_driver_audiodec.a
|
||||
|
||||
libneutrino_driver_audiodec_a_SOURCES = \
|
||||
basedec.cpp \
|
||||
cdrdec.cpp \
|
||||
mp3dec.cpp \
|
||||
oggdec.cpp \
|
||||
wavdec.cpp \
|
||||
tag.c \
|
||||
crc.c \
|
||||
vis.cpp
|
241
src/driver/audiodec/basedec.cpp
Normal file
241
src/driver/audiodec/basedec.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2004 Zwen
|
||||
base decoder class
|
||||
Homepage: http://www.cyberphoria.org/
|
||||
|
||||
Kommentar:
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <basedec.h>
|
||||
#include <cdrdec.h>
|
||||
#include <mp3dec.h>
|
||||
#include <oggdec.h>
|
||||
#include <wavdec.h>
|
||||
#include <linux/soundcard.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <driver/netfile.h>
|
||||
|
||||
#include <driver/audioplay.h> // for ShoutcastCallback()
|
||||
|
||||
#include <global.h>
|
||||
#include <neutrino.h>
|
||||
#include <zapit/client/zapittools.h>
|
||||
|
||||
unsigned int CBaseDec::mSamplerate=0;
|
||||
|
||||
void ShoutcastCallback(void *arg)
|
||||
{
|
||||
CAudioPlayer::getInstance()->sc_callback(arg);
|
||||
}
|
||||
|
||||
CBaseDec::RetCode CBaseDec::DecoderBase(CAudiofile* const in,
|
||||
const int OutputFd, State* const state,
|
||||
time_t* const t,
|
||||
unsigned int* const secondsToSkip)
|
||||
{
|
||||
RetCode Status = OK;
|
||||
|
||||
FILE* fp = fopen( in->Filename.c_str(), "r" );
|
||||
if ( fp == NULL )
|
||||
{
|
||||
fprintf( stderr, "Error opening file %s for decoding.\n",
|
||||
in->Filename.c_str() );
|
||||
Status = INTERNAL_ERR;
|
||||
}
|
||||
/* jump to first audio frame; audio_start_pos is only set for FILE_MP3 */
|
||||
else if ( in->MetaData.audio_start_pos &&
|
||||
fseek( fp, in->MetaData.audio_start_pos, SEEK_SET ) == -1 )
|
||||
{
|
||||
fprintf( stderr, "fseek() failed.\n" );
|
||||
Status = INTERNAL_ERR;
|
||||
}
|
||||
|
||||
if ( Status == OK )
|
||||
{
|
||||
if( in->FileType == CFile::STREAM_AUDIO )
|
||||
{
|
||||
if ( fstatus( fp, ShoutcastCallback ) < 0 )
|
||||
{
|
||||
fprintf( stderr, "Error adding shoutcast callback: %s",
|
||||
err_txt );
|
||||
}
|
||||
if(ftype(fp, (char *) "ogg"))
|
||||
{
|
||||
Status = COggDec::getInstance()->Decoder( fp, OutputFd, state,
|
||||
&in->MetaData, t,
|
||||
secondsToSkip );
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state,
|
||||
&in->MetaData, t,
|
||||
secondsToSkip );
|
||||
}
|
||||
}
|
||||
else if( in->FileType == CFile::FILE_MP3)
|
||||
{
|
||||
Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state,
|
||||
&in->MetaData, t,
|
||||
secondsToSkip );
|
||||
}
|
||||
else if( in->FileType == CFile::FILE_OGG )
|
||||
{
|
||||
Status = COggDec::getInstance()->Decoder( fp, OutputFd, state,
|
||||
&in->MetaData, t,
|
||||
secondsToSkip );
|
||||
}
|
||||
else if( in->FileType == CFile::FILE_WAV )
|
||||
{
|
||||
Status = CWavDec::getInstance()->Decoder( fp, OutputFd, state,
|
||||
&in->MetaData, t,
|
||||
secondsToSkip );
|
||||
}
|
||||
else if( in->FileType == CFile::FILE_CDR )
|
||||
{
|
||||
Status = CCdrDec::getInstance()->Decoder( fp, OutputFd, state,
|
||||
&in->MetaData, t,
|
||||
secondsToSkip );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "DecoderBase: Supplied filetype is not " );
|
||||
fprintf( stderr, "supported by Audioplayer.\n" );
|
||||
Status = INTERNAL_ERR;
|
||||
}
|
||||
|
||||
if ( fclose( fp ) == EOF )
|
||||
{
|
||||
fprintf( stderr, "Could not close file %s.\n",
|
||||
in->Filename.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
bool CBaseDec::GetMetaDataBase(CAudiofile* const in, const bool nice)
|
||||
{
|
||||
bool Status = true;
|
||||
|
||||
if ( in->FileType == CFile::FILE_MP3 || in->FileType == CFile::FILE_OGG ||
|
||||
in->FileType == CFile::FILE_WAV || in->FileType == CFile::FILE_CDR )
|
||||
{
|
||||
FILE* fp = fopen( in->Filename.c_str(), "r" );
|
||||
if ( fp == NULL )
|
||||
{
|
||||
fprintf( stderr, "Error opening file %s for meta data reading.\n",
|
||||
in->Filename.c_str() );
|
||||
Status = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(in->FileType == CFile::FILE_MP3)
|
||||
{
|
||||
Status = CMP3Dec::getInstance()->GetMetaData(fp, nice,
|
||||
&in->MetaData);
|
||||
}
|
||||
else if(in->FileType == CFile::FILE_OGG)
|
||||
{
|
||||
Status = COggDec::getInstance()->GetMetaData(fp, nice,
|
||||
&in->MetaData);
|
||||
}
|
||||
else if(in->FileType == CFile::FILE_WAV)
|
||||
{
|
||||
Status = CWavDec::getInstance()->GetMetaData(fp, nice,
|
||||
&in->MetaData);
|
||||
}
|
||||
else if(in->FileType == CFile::FILE_CDR)
|
||||
{
|
||||
Status = CCdrDec::getInstance()->GetMetaData(fp, nice,
|
||||
&in->MetaData);
|
||||
}
|
||||
|
||||
if ( fclose( fp ) == EOF )
|
||||
{
|
||||
fprintf( stderr, "Could not close file %s.\n",
|
||||
in->Filename.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "GetMetaDataBase: Filetype is not supported for " );
|
||||
fprintf( stderr, "meta data reading.\n" );
|
||||
Status = false;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
bool CBaseDec::SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels)
|
||||
{
|
||||
bool crit_error=false;
|
||||
|
||||
if (::ioctl(soundfd, SNDCTL_DSP_RESET))
|
||||
printf("reset failed\n");
|
||||
if(::ioctl(soundfd, SNDCTL_DSP_SETFMT, &fmt))
|
||||
printf("setfmt failed\n");
|
||||
if(::ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels))
|
||||
printf("channel set failed\n");
|
||||
if (dsp_speed != mSamplerate)
|
||||
{
|
||||
// mute audio to reduce pops when changing samplerate (avia_reset)
|
||||
//bool was_muted = avs_mute(true);
|
||||
if (::ioctl(soundfd, SNDCTL_DSP_SPEED, &dsp_speed))
|
||||
{
|
||||
printf("speed set failed\n");
|
||||
crit_error=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if 0
|
||||
unsigned int rs = 0;
|
||||
::ioctl(soundfd, SNDCTL_DSP_SPEED, &rs);
|
||||
mSamplerate = dsp_speed;
|
||||
// disable iec aka digi out (avia reset enables it again)
|
||||
//g_Zapit->IecOff();
|
||||
#endif
|
||||
}
|
||||
//usleep(400000);
|
||||
//if (!was_muted)
|
||||
// avs_mute(false);
|
||||
}
|
||||
//printf("Debug: SNDCTL_DSP_RESET %d / SNDCTL_DSP_SPEED %d / SNDCTL_DSP_CHANNELS %d / SNDCTL_DSP_SETFMT %d\n",
|
||||
// SNDCTL_DSP_RESET, SNDCTL_DSP_SPEED, SNDCTL_DSP_CHANNELS, SNDCTL_DSP_SETFMT);
|
||||
return crit_error;
|
||||
}
|
||||
|
||||
bool CBaseDec::avs_mute(bool mute)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBaseDec::Init()
|
||||
{
|
||||
mSamplerate=0;
|
||||
}
|
||||
|
63
src/driver/audiodec/basedec.h
Normal file
63
src/driver/audiodec/basedec.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2004 Zwen
|
||||
|
||||
Decoder base class
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __BASE_DEC__
|
||||
#define __BASE_DEC__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <driver/audiofile.h>
|
||||
#include <driver/audiometadata.h>
|
||||
|
||||
class CBaseDec
|
||||
{
|
||||
public:
|
||||
enum State {STOP = 0, STOP_REQ, PLAY, PAUSE, FF, REV};
|
||||
enum RetCode { OK = 0, READ_ERR, WRITE_ERR, DSPSET_ERR, DATA_ERR, INTERNAL_ERR };
|
||||
|
||||
// the follwing two methods have to be implemented for new decoders
|
||||
//@param secondsToSkip: a value of 0 indicates that normal FF/REV operation was requested
|
||||
// a value > 0 indicates that *one* jump forwards (FF) or backwards (REV) was requested
|
||||
virtual RetCode Decoder(FILE *, const int, State* const, CAudioMetaData*, time_t* const, unsigned int* const)=0;
|
||||
virtual bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m)=0;
|
||||
|
||||
static RetCode DecoderBase(CAudiofile* const in, const int OutputFd,
|
||||
State* const state, time_t* const t,
|
||||
unsigned int* const secondsToSkip);
|
||||
static bool GetMetaDataBase(CAudiofile* const in, const bool nice);
|
||||
static void Init();
|
||||
|
||||
CBaseDec(){};
|
||||
static bool SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels);
|
||||
private:
|
||||
static bool avs_mute(bool mute);
|
||||
unsigned static int mSamplerate;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
65
src/driver/audiodec/cdrdec.cpp
Normal file
65
src/driver/audiodec/cdrdec.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2004 thegoodguy
|
||||
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
cdr audio decoder
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <cdrdec.h>
|
||||
|
||||
CCdrDec* CCdrDec::getInstance()
|
||||
{
|
||||
static CCdrDec* CdrDec = NULL;
|
||||
if (CdrDec == NULL)
|
||||
{
|
||||
CdrDec = new CCdrDec();
|
||||
}
|
||||
return CdrDec;
|
||||
}
|
||||
|
||||
bool CCdrDec::SetMetaData(FILE* in, CAudioMetaData* m)
|
||||
{
|
||||
header_size = 0;
|
||||
|
||||
fseek(in, 0, SEEK_END);
|
||||
int filesize = ftell(in);
|
||||
|
||||
m->type = CAudioMetaData::CDR;
|
||||
m->bitrate = 44100 * 2 * 2 * 8;
|
||||
m->samplerate = 44100;
|
||||
mBitsPerSample = 16;
|
||||
mChannels = 2;
|
||||
m->total_time = filesize / (44100 * 2 * 2);
|
||||
m->type_info = "CDR / 2 channels / 16 bit";
|
||||
m->changed=true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
43
src/driver/audiodec/cdrdec.h
Normal file
43
src/driver/audiodec/cdrdec.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2004 thegoodguy
|
||||
|
||||
cdr audio decoder
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __CDR_DEC__
|
||||
#define __CDR_DEC__
|
||||
|
||||
#include <driver/audiodec/wavdec.h>
|
||||
|
||||
class CCdrDec : public CWavDec
|
||||
{
|
||||
public:
|
||||
static CCdrDec* getInstance();
|
||||
protected:
|
||||
virtual bool SetMetaData(FILE* in, CAudioMetaData* m);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
109
src/driver/audiodec/crc.c
Normal file
109
src/driver/audiodec/crc.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* madplay - MPEG audio decoder and player
|
||||
* Copyright (C) 2000-2004 Robert Leslie
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: crc.c,v 1.1 2004/08/28 14:11:58 rasc Exp $
|
||||
*/
|
||||
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
//# include "global.h"
|
||||
|
||||
# include "crc.h"
|
||||
|
||||
/*
|
||||
* It would be nice to use the code in libmad to compute all CRC-16 values,
|
||||
* but unfortunately the LAME tag uses reflected CRCs whereas MPEG audio
|
||||
* frames do not. Therefore we are forced to write yet another CRC routine.
|
||||
*/
|
||||
|
||||
static
|
||||
unsigned short const crc_table[256] = {
|
||||
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
|
||||
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
|
||||
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
|
||||
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
|
||||
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
|
||||
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
|
||||
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
|
||||
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
|
||||
|
||||
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
|
||||
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
|
||||
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
|
||||
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
|
||||
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
|
||||
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
|
||||
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
|
||||
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
|
||||
|
||||
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
|
||||
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
|
||||
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
|
||||
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
|
||||
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
|
||||
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
|
||||
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
|
||||
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
|
||||
|
||||
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
|
||||
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
|
||||
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
|
||||
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
|
||||
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
|
||||
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
|
||||
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
|
||||
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
|
||||
};
|
||||
|
||||
/*
|
||||
* NAME: crc->compute()
|
||||
* DESCRIPTION: calculate reflected CRC-16 value (polynomial 0x8005)
|
||||
*/
|
||||
unsigned short crc_compute(char const *data, unsigned int length,
|
||||
unsigned short init)
|
||||
{
|
||||
register unsigned int crc;
|
||||
|
||||
for (crc = init; length >= 8; length -= 8) {
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
switch (length) {
|
||||
case 7: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
case 6: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
case 5: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
case 4: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
case 3: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
case 2: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
case 1: crc = crc_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
|
||||
case 0: break;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
29
src/driver/audiodec/crc.h
Normal file
29
src/driver/audiodec/crc.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* madplay - MPEG audio decoder and player
|
||||
* Copyright (C) 2000-2004 Robert Leslie
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: crc.h,v 1.1 2004/08/28 14:11:58 rasc Exp $
|
||||
*/
|
||||
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
# ifndef __CRC_H__
|
||||
# define __CRC_H__
|
||||
|
||||
unsigned short crc_compute(char const *, unsigned int, unsigned short);
|
||||
|
||||
# endif /* __CRC_H__ */
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
274
src/driver/audiodec/fft.c
Normal file
274
src/driver/audiodec/fft.c
Normal file
@@ -0,0 +1,274 @@
|
||||
/* fft.c: Iterative implementation of a FFT
|
||||
* Copyright (C) 1999 Richard Boulton <richard@tartarus.org>
|
||||
* Convolution stuff by Ralph Loader <suckfish@ihug.co.nz>
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* Remove compiling in of FFT_BUFFER_SIZE? (Might slow things down, but would
|
||||
* be nice to be able to change size at runtime.)
|
||||
* Finish making / checking thread-safety.
|
||||
* More optimisations.
|
||||
*/
|
||||
|
||||
#include "fft.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#ifndef PI
|
||||
#ifdef M_PI
|
||||
#define PI M_PI
|
||||
#else
|
||||
#define PI 3.14159265358979323846 /* pi */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* ########### */
|
||||
/* # Structs # */
|
||||
/* ########### */
|
||||
|
||||
struct _struct_fft_state {
|
||||
/* Temporary data stores to perform FFT in. */
|
||||
float real[FFT_BUFFER_SIZE];
|
||||
float imag[FFT_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
/* ############################# */
|
||||
/* # Local function prototypes # */
|
||||
/* ############################# */
|
||||
|
||||
static void fft_prepare(const sound_sample *input, float * re, float * im);
|
||||
static void fft_calculate(float * re, float * im);
|
||||
static void fft_output(const float *re, const float *im, float *output);
|
||||
static int reverseBits(unsigned int initial);
|
||||
|
||||
/* #################### */
|
||||
/* # Global variables # */
|
||||
/* #################### */
|
||||
|
||||
/* Table to speed up bit reverse copy */
|
||||
static unsigned int bitReverse[FFT_BUFFER_SIZE];
|
||||
|
||||
/* The next two tables could be made to use less space in memory, since they
|
||||
* overlap hugely, but hey. */
|
||||
static float sintable[FFT_BUFFER_SIZE / 2];
|
||||
static float costable[FFT_BUFFER_SIZE / 2];
|
||||
|
||||
/* ############################## */
|
||||
/* # Externally called routines # */
|
||||
/* ############################## */
|
||||
|
||||
/* --------- */
|
||||
/* FFT stuff */
|
||||
/* --------- */
|
||||
|
||||
/*
|
||||
* Initialisation routine - sets up tables and space to work in.
|
||||
* Returns a pointer to internal state, to be used when performing calls.
|
||||
* On error, returns NULL.
|
||||
* The pointer should be freed when it is finished with, by fft_close().
|
||||
*/
|
||||
fft_state *fft_init(void) {
|
||||
fft_state *state;
|
||||
unsigned int i;
|
||||
|
||||
state = (fft_state *) malloc (sizeof(fft_state));
|
||||
if(!state) return NULL;
|
||||
|
||||
for(i = 0; i < FFT_BUFFER_SIZE; i++) {
|
||||
bitReverse[i] = reverseBits(i);
|
||||
}
|
||||
for(i = 0; i < FFT_BUFFER_SIZE / 2; i++) {
|
||||
float j = 2 * PI * i / FFT_BUFFER_SIZE;
|
||||
costable[i] = cos(j);
|
||||
sintable[i] = sin(j);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do all the steps of the FFT, taking as input sound data (as described in
|
||||
* sound.h) and returning the intensities of each frequency as floats in the
|
||||
* range 0 to ((FFT_BUFFER_SIZE / 2) * 32768) ^ 2
|
||||
*
|
||||
* FIXME - the above range assumes no frequencies present have an amplitude
|
||||
* larger than that of the sample variation. But this is false: we could have
|
||||
* a wave such that its maximums are always between samples, and it's just
|
||||
* inside the representable range at the places samples get taken.
|
||||
* Question: what _is_ the maximum value possible. Twice that value? Root
|
||||
* two times that value? Hmmm. Think it depends on the frequency, too.
|
||||
*
|
||||
* The input array is assumed to have FFT_BUFFER_SIZE elements,
|
||||
* and the output array is assumed to have (FFT_BUFFER_SIZE / 2 + 1) elements.
|
||||
* state is a (non-NULL) pointer returned by fft_init.
|
||||
*/
|
||||
void fft_perform(const sound_sample *input, float *output, fft_state *state) {
|
||||
/* Convert data from sound format to be ready for FFT */
|
||||
fft_prepare(input, state->real, state->imag);
|
||||
|
||||
/* Do the actual FFT */
|
||||
fft_calculate(state->real, state->imag);
|
||||
|
||||
/* Convert the FFT output into intensities */
|
||||
fft_output(state->real, state->imag, output);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free the state.
|
||||
*/
|
||||
void fft_close(fft_state *state) {
|
||||
if(state) free(state);
|
||||
}
|
||||
|
||||
/* ########################### */
|
||||
/* # Locally called routines # */
|
||||
/* ########################### */
|
||||
|
||||
/*
|
||||
* Prepare data to perform an FFT on
|
||||
*/
|
||||
static void fft_prepare(const sound_sample *input, float * re, float * im) {
|
||||
unsigned int i;
|
||||
float *realptr = re;
|
||||
float *imagptr = im;
|
||||
|
||||
/* Get input, in reverse bit order */
|
||||
for(i = 0; i < FFT_BUFFER_SIZE; i++) {
|
||||
*realptr++ = input[bitReverse[i]];
|
||||
*imagptr++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Take result of an FFT and calculate the intensities of each frequency
|
||||
* Note: only produces half as many data points as the input had.
|
||||
* This is roughly a consequence of the Nyquist sampling theorm thingy.
|
||||
* (FIXME - make this comment better, and helpful.)
|
||||
*
|
||||
* The two divisions by 4 are also a consequence of this: the contributions
|
||||
* returned for each frequency are split into two parts, one at i in the
|
||||
* table, and the other at FFT_BUFFER_SIZE - i, except for i = 0 and
|
||||
* FFT_BUFFER_SIZE which would otherwise get float (and then 4* when squared)
|
||||
* the contributions.
|
||||
*/
|
||||
static void fft_output(const float * re, const float * im, float *output) {
|
||||
float *outputptr = output;
|
||||
const float *realptr = re;
|
||||
const float *imagptr = im;
|
||||
float *endptr = output + FFT_BUFFER_SIZE / 2;
|
||||
|
||||
#ifdef DEBUG
|
||||
unsigned int i, j;
|
||||
#endif
|
||||
|
||||
while(outputptr <= endptr) {
|
||||
*outputptr = (*realptr * *realptr) + (*imagptr * *imagptr);
|
||||
outputptr++; realptr++; imagptr++;
|
||||
}
|
||||
/* Do divisions to keep the constant and highest frequency terms in scale
|
||||
* with the other terms. */
|
||||
*output /= 4;
|
||||
*endptr /= 4;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Recalculated input:\n");
|
||||
for(i = 0; i < FFT_BUFFER_SIZE; i++) {
|
||||
float val_real = 0;
|
||||
float val_imag = 0;
|
||||
for(j = 0; j < FFT_BUFFER_SIZE; j++) {
|
||||
float fact_real = cos(- 2 * j * i * PI / FFT_BUFFER_SIZE);
|
||||
float fact_imag = sin(- 2 * j * i * PI / FFT_BUFFER_SIZE);
|
||||
val_real += fact_real * re[j] - fact_imag * im[j];
|
||||
val_imag += fact_real * im[j] + fact_imag * re[j];
|
||||
}
|
||||
printf("%5d = %8f + i * %8f\n", i,
|
||||
val_real / FFT_BUFFER_SIZE,
|
||||
val_imag / FFT_BUFFER_SIZE);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually perform the FFT
|
||||
*/
|
||||
static void fft_calculate(float * re, float * im) {
|
||||
unsigned int i, j, k;
|
||||
unsigned int exchanges;
|
||||
float fact_real, fact_imag;
|
||||
float tmp_real, tmp_imag;
|
||||
unsigned int factfact;
|
||||
|
||||
/* Set up some variables to reduce calculation in the loops */
|
||||
exchanges = 1;
|
||||
factfact = FFT_BUFFER_SIZE / 2;
|
||||
|
||||
/* Loop through the divide and conquer steps */
|
||||
for(i = FFT_BUFFER_SIZE_LOG; i != 0; i--) {
|
||||
/* In this step, we have 2 ^ (i - 1) exchange groups, each with
|
||||
* 2 ^ (FFT_BUFFER_SIZE_LOG - i) exchanges
|
||||
*/
|
||||
/* Loop through the exchanges in a group */
|
||||
for(j = 0; j != exchanges; j++) {
|
||||
/* Work out factor for this exchange
|
||||
* factor ^ (exchanges) = -1
|
||||
* So, real = cos(j * PI / exchanges),
|
||||
* imag = sin(j * PI / exchanges)
|
||||
*/
|
||||
fact_real = costable[j * factfact];
|
||||
fact_imag = sintable[j * factfact];
|
||||
|
||||
/* Loop through all the exchange groups */
|
||||
for(k = j; k < FFT_BUFFER_SIZE; k += exchanges << 1) {
|
||||
int k1 = k + exchanges;
|
||||
/* newval[k] := val[k] + factor * val[k1]
|
||||
* newval[k1] := val[k] - factor * val[k1]
|
||||
**/
|
||||
#ifdef DEBUG
|
||||
printf("%d %d %d\n", i,j,k);
|
||||
printf("Exchange %d with %d\n", k, k1);
|
||||
printf("Factor %9f + i * %8f\n", fact_real, fact_imag);
|
||||
#endif
|
||||
/* FIXME - potential scope for more optimization here? */
|
||||
tmp_real = fact_real * re[k1] - fact_imag * im[k1];
|
||||
tmp_imag = fact_real * im[k1] + fact_imag * re[k1];
|
||||
re[k1] = re[k] - tmp_real;
|
||||
im[k1] = im[k] - tmp_imag;
|
||||
re[k] += tmp_real;
|
||||
im[k] += tmp_imag;
|
||||
#ifdef DEBUG
|
||||
for(k1 = 0; k1 < FFT_BUFFER_SIZE; k1++) {
|
||||
printf("%5d = %8f + i * %8f\n", k1, real[k1], imag[k1]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
exchanges <<= 1;
|
||||
factfact >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int reverseBits(unsigned int initial) {
|
||||
unsigned int reversed = 0, loop;
|
||||
for(loop = 0; loop < FFT_BUFFER_SIZE_LOG; loop++) {
|
||||
reversed <<= 1;
|
||||
reversed += (initial & 1);
|
||||
initial >>= 1;
|
||||
}
|
||||
return reversed;
|
||||
}
|
43
src/driver/audiodec/fft.h
Normal file
43
src/driver/audiodec/fft.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* fft.h: Header for iterative implementation of a FFT
|
||||
* Copyright (C) 1999 Richard Boulton <richard@tartarus.org>
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _FFT_H_
|
||||
#define _FFT_H_
|
||||
|
||||
#define FFT_BUFFER_SIZE_LOG 9
|
||||
|
||||
#define FFT_BUFFER_SIZE (1 << FFT_BUFFER_SIZE_LOG)
|
||||
|
||||
/* sound sample - should be an signed 16 bit value */
|
||||
typedef short int sound_sample;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* FFT library */
|
||||
typedef struct _struct_fft_state fft_state;
|
||||
fft_state *fft_init (void);
|
||||
void fft_perform (const sound_sample *input, float *output, fft_state *state);
|
||||
void fft_close (fft_state *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FFT_H_ */
|
528
src/driver/audiodec/int_fft.c
Normal file
528
src/driver/audiodec/int_fft.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/* fix_fft.c - Fixed-point Fast Fourier Transform */
|
||||
/*
|
||||
fix_fft() perform FFT or inverse FFT
|
||||
window() applies a Hanning window to the (time) input
|
||||
fix_loud() calculates the loudness of the signal, for
|
||||
each freq point. Result is an integer array,
|
||||
units are dB (values will be negative).
|
||||
iscale() scale an integer value by (numer/denom).
|
||||
fix_mpy() perform fixed-point multiplication.
|
||||
Sinewave[1024] sinewave normalized to 32767 (= 1.0).
|
||||
Loudampl[100] Amplitudes for lopudnesses from 0 to -99 dB.
|
||||
Low_pass Low-pass filter, cutoff at sample_freq / 4.
|
||||
|
||||
|
||||
All data are fixed-point short integers, in which
|
||||
-32768 to +32768 represent -1.0 to +1.0. Integer arithmetic
|
||||
is used for speed, instead of the more natural floating-point.
|
||||
|
||||
For the forward FFT (time -> freq), fixed scaling is
|
||||
performed to prevent arithmetic overflow, and to map a 0dB
|
||||
sine/cosine wave (i.e. amplitude = 32767) to two -6dB freq
|
||||
coefficients; the one in the lower half is reported as 0dB
|
||||
by fix_loud(). The return value is always 0.
|
||||
|
||||
For the inverse FFT (freq -> time), fixed scaling cannot be
|
||||
done, as two 0dB coefficients would sum to a peak amplitude of
|
||||
64K, overflowing the 32k range of the fixed-point integers.
|
||||
Thus, the fix_fft() routine performs variable scaling, and
|
||||
returns a value which is the number of bits LEFT by which
|
||||
the output must be shifted to get the actual amplitude
|
||||
(i.e. if fix_fft() returns 3, each value of fr[] and fi[]
|
||||
must be multiplied by 8 (2**3) for proper scaling.
|
||||
Clearly, this cannot be done within the fixed-point short
|
||||
integers. In practice, if the result is to be used as a
|
||||
filter, the scale_shift can usually be ignored, as the
|
||||
result will be approximately correctly normalized as is.
|
||||
|
||||
|
||||
TURBO C, any memory model; uses inline assembly for speed
|
||||
and for carefully-scaled arithmetic.
|
||||
|
||||
Written by: Tom Roberts 11/8/89
|
||||
Made portable: Malcolm Slaney 12/15/94 malcolm@interval.com
|
||||
|
||||
Timing on a Macintosh PowerBook 180.... (using Symantec C6.0)
|
||||
fix_fft (1024 points) 8 ticks
|
||||
fft (1024 points - Using SANE) 112 Ticks
|
||||
fft (1024 points - Using FPU) 11
|
||||
|
||||
*/
|
||||
|
||||
/* FIX_MPY() - fixed-point multiplication macro.
|
||||
This macro is a statement, not an expression (uses asm).
|
||||
BEWARE: make sure _DX is not clobbered by evaluating (A) or DEST.
|
||||
args are all of type fixed.
|
||||
Scaling ensures that 32767*32767 = 32767. */
|
||||
#define dosFIX_MPY(DEST,A,B) { \
|
||||
_DX = (B); \
|
||||
_AX = (A); \
|
||||
asm imul dx; \
|
||||
asm add ax,ax; \
|
||||
asm adc dx,dx; \
|
||||
DEST = _DX; }
|
||||
|
||||
#define FIX_MPY(DEST,A,B) DEST = ((long)(A) * (long)(B))>>15
|
||||
|
||||
#define N_WAVE 1024 /* dimension of Sinewave[] */
|
||||
#define LOG2_N_WAVE 10 /* log2(N_WAVE) */
|
||||
#define N_LOUD 100 /* dimension of Loudampl[] */
|
||||
#ifndef fixed
|
||||
#define fixed short
|
||||
#endif
|
||||
|
||||
extern fixed Sinewave[N_WAVE]; /* placed at end of this file for clarity */
|
||||
extern fixed Loudampl[N_LOUD];
|
||||
int db_from_ampl(fixed re, fixed im);
|
||||
fixed fix_mpy(fixed a, fixed b);
|
||||
|
||||
/*
|
||||
fix_fft() - perform fast Fourier transform.
|
||||
|
||||
if n>0 FFT is done, if n<0 inverse FFT is done
|
||||
fr[n],fi[n] are real,imaginary arrays, INPUT AND RESULT.
|
||||
size of data = 2**m
|
||||
set inverse to 0=dft, 1=idft
|
||||
*/
|
||||
int fix_fft(fixed fr[], fixed fi[], int m, int inverse)
|
||||
{
|
||||
int mr,nn,i,j,l,k,istep, n, scale, shift;
|
||||
fixed qr,qi,tr,ti,wr,wi;
|
||||
|
||||
n = 1<<m;
|
||||
|
||||
if(n > N_WAVE)
|
||||
return -1;
|
||||
|
||||
mr = 0;
|
||||
nn = n - 1;
|
||||
scale = 0;
|
||||
|
||||
/* decimation in time - re-order data */
|
||||
for(m=1; m<=nn; ++m) {
|
||||
l = n;
|
||||
do {
|
||||
l >>= 1;
|
||||
} while(mr+l > nn);
|
||||
mr = (mr & (l-1)) + l;
|
||||
|
||||
if(mr <= m) continue;
|
||||
tr = fr[m];
|
||||
fr[m] = fr[mr];
|
||||
fr[mr] = tr;
|
||||
ti = fi[m];
|
||||
fi[m] = fi[mr];
|
||||
fi[mr] = ti;
|
||||
}
|
||||
|
||||
l = 1;
|
||||
k = LOG2_N_WAVE-1;
|
||||
while(l < n) {
|
||||
if(inverse) {
|
||||
/* variable scaling, depending upon data */
|
||||
shift = 0;
|
||||
for(i=0; i<n; ++i) {
|
||||
j = fr[i];
|
||||
if(j < 0)
|
||||
j = -j;
|
||||
m = fi[i];
|
||||
if(m < 0)
|
||||
m = -m;
|
||||
if(j > 16383 || m > 16383) {
|
||||
shift = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(shift)
|
||||
++scale;
|
||||
} else {
|
||||
/* fixed scaling, for proper normalization -
|
||||
there will be log2(n) passes, so this
|
||||
results in an overall factor of 1/n,
|
||||
distributed to maximize arithmetic accuracy. */
|
||||
shift = 1;
|
||||
}
|
||||
/* it may not be obvious, but the shift will be performed
|
||||
on each data point exactly once, during this pass. */
|
||||
istep = l << 1;
|
||||
for(m=0; m<l; ++m) {
|
||||
j = m << k;
|
||||
/* 0 <= j < N_WAVE/2 */
|
||||
wr = Sinewave[j+N_WAVE/4];
|
||||
wi = -Sinewave[j];
|
||||
if(inverse)
|
||||
wi = -wi;
|
||||
if(shift) {
|
||||
wr >>= 1;
|
||||
wi >>= 1;
|
||||
}
|
||||
for(i=m; i<n; i+=istep) {
|
||||
j = i + l;
|
||||
tr = fix_mpy(wr,fr[j]) -
|
||||
fix_mpy(wi,fi[j]);
|
||||
ti = fix_mpy(wr,fi[j]) +
|
||||
fix_mpy(wi,fr[j]);
|
||||
qr = fr[i];
|
||||
qi = fi[i];
|
||||
if(shift) {
|
||||
qr >>= 1;
|
||||
qi >>= 1;
|
||||
}
|
||||
fr[j] = qr - tr;
|
||||
fi[j] = qi - ti;
|
||||
fr[i] = qr + tr;
|
||||
fi[i] = qi + ti;
|
||||
}
|
||||
}
|
||||
--k;
|
||||
l = istep;
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
|
||||
/* window() - apply a Hanning window */
|
||||
void window(fixed fr[], int n)
|
||||
{
|
||||
int i,j,k;
|
||||
|
||||
j = N_WAVE/n;
|
||||
n >>= 1;
|
||||
for(i=0,k=N_WAVE/4; i<n; ++i,k+=j)
|
||||
FIX_MPY(fr[i],fr[i],16384-(Sinewave[k]>>1));
|
||||
n <<= 1;
|
||||
for(k-=j; i<n; ++i,k-=j)
|
||||
FIX_MPY(fr[i],fr[i],16384-(Sinewave[k]>>1));
|
||||
}
|
||||
|
||||
/* fix_loud() - compute loudness of freq-spectrum components.
|
||||
n should be ntot/2, where ntot was passed to fix_fft();
|
||||
6 dB is added to account for the omitted alias components.
|
||||
scale_shift should be the result of fix_fft(), if the time-series
|
||||
was obtained from an inverse FFT, 0 otherwise.
|
||||
loud[] is the loudness, in dB wrt 32767; will be +10 to -N_LOUD.
|
||||
*/
|
||||
void fix_loud(fixed loud[], fixed fr[], fixed fi[], int n, int scale_shift)
|
||||
{
|
||||
int i, max;
|
||||
|
||||
max = 0;
|
||||
if(scale_shift > 0)
|
||||
max = 10;
|
||||
scale_shift = (scale_shift+1) * 6;
|
||||
|
||||
for(i=0; i<n; ++i) {
|
||||
loud[i] = db_from_ampl(fr[i],fi[i]) + scale_shift;
|
||||
if(loud[i] > max)
|
||||
loud[i] = max;
|
||||
}
|
||||
}
|
||||
|
||||
/* db_from_ampl() - find loudness (in dB) from
|
||||
the complex amplitude.
|
||||
*/
|
||||
int db_from_ampl(fixed re, fixed im)
|
||||
{
|
||||
static long loud2[N_LOUD] = {0};
|
||||
long v;
|
||||
int i;
|
||||
|
||||
if(loud2[0] == 0) {
|
||||
loud2[0] = (long)Loudampl[0] * (long)Loudampl[0];
|
||||
for(i=1; i<N_LOUD; ++i) {
|
||||
v = (long)Loudampl[i] * (long)Loudampl[i];
|
||||
loud2[i] = v;
|
||||
loud2[i-1] = (loud2[i-1]+v) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
v = (long)re * (long)re + (long)im * (long)im;
|
||||
|
||||
for(i=0; i<N_LOUD; ++i)
|
||||
if(loud2[i] <= v)
|
||||
break;
|
||||
|
||||
return (-i);
|
||||
}
|
||||
|
||||
/*
|
||||
fix_mpy() - fixed-point multiplication
|
||||
*/
|
||||
fixed fix_mpy(fixed a, fixed b)
|
||||
{
|
||||
FIX_MPY(a,a,b);
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
iscale() - scale an integer value by (numer/denom)
|
||||
*/
|
||||
int iscale(int value, int numer, int denom)
|
||||
{
|
||||
#ifdef DOS
|
||||
asm mov ax,value
|
||||
asm imul WORD PTR numer
|
||||
asm idiv WORD PTR denom
|
||||
|
||||
return _AX;
|
||||
#else
|
||||
return (long) value * (long)numer/(long)denom;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
fix_dot() - dot product of two fixed arrays
|
||||
*/
|
||||
fixed fix_dot(fixed *hpa, fixed *pb, int n)
|
||||
{
|
||||
fixed *pa;
|
||||
long sum;
|
||||
register fixed a,b;
|
||||
//unsigned int seg,off;
|
||||
|
||||
/* seg = FP_SEG(hpa);
|
||||
off = FP_OFF(hpa);
|
||||
seg += off>>4;
|
||||
off &= 0x000F;
|
||||
pa = MK_FP(seg,off);
|
||||
*/
|
||||
sum = 0L;
|
||||
while(n--) {
|
||||
a = *pa++;
|
||||
b = *pb++;
|
||||
FIX_MPY(a,a,b);
|
||||
sum += a;
|
||||
}
|
||||
|
||||
if(sum > 0x7FFF)
|
||||
sum = 0x7FFF;
|
||||
else if(sum < -0x7FFF)
|
||||
sum = -0x7FFF;
|
||||
|
||||
return (fixed)sum;
|
||||
#ifdef DOS
|
||||
/* ASSUMES hpa is already normalized so FP_OFF(hpa) < 16 */
|
||||
asm push ds
|
||||
asm lds si,hpa
|
||||
asm les di,pb
|
||||
asm xor bx,bx
|
||||
|
||||
asm xor cx,cx
|
||||
|
||||
loop: /* intermediate values can overflow by a factor of 2 without
|
||||
causing an error; the final value must not overflow! */
|
||||
asm lodsw
|
||||
.
|
||||
asm imul word ptr es:[di]
|
||||
asm add bx,ax
|
||||
asm adc cx,dx
|
||||
asm jo overflow
|
||||
asm add di,2
|
||||
asm dec word ptr n
|
||||
asm jg loop
|
||||
|
||||
asm add bx,bx
|
||||
asm adc cx,cx
|
||||
asm jo overflow
|
||||
|
||||
asm pop ds
|
||||
return _CX;
|
||||
|
||||
overflow:
|
||||
asm mov cx,7FFFH
|
||||
asm adc cx,0
|
||||
|
||||
asm pop ds
|
||||
return _CX;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if N_WAVE != 1024
|
||||
ERROR: N_WAVE != 1024
|
||||
#endif
|
||||
fixed Sinewave[1024] = {
|
||||
0, 201, 402, 603, 804, 1005, 1206, 1406,
|
||||
1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011,
|
||||
3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608,
|
||||
4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195,
|
||||
6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766,
|
||||
7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319,
|
||||
9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849,
|
||||
11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353,
|
||||
12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827,
|
||||
14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268,
|
||||
15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672,
|
||||
16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036,
|
||||
18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357,
|
||||
19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631,
|
||||
20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855,
|
||||
22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027,
|
||||
23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143,
|
||||
24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201,
|
||||
25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198,
|
||||
26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132,
|
||||
27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001,
|
||||
28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802,
|
||||
28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534,
|
||||
29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195,
|
||||
30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783,
|
||||
30851, 30918, 30984, 31049,
|
||||
31113, 31175, 31236, 31297,
|
||||
31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735,
|
||||
31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097,
|
||||
32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382,
|
||||
32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588,
|
||||
32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717,
|
||||
32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766,
|
||||
32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736,
|
||||
32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628,
|
||||
32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441,
|
||||
32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176,
|
||||
32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833,
|
||||
31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413,
|
||||
31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918,
|
||||
30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349,
|
||||
30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706,
|
||||
29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992,
|
||||
28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208,
|
||||
28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355,
|
||||
27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437,
|
||||
26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456,
|
||||
25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413,
|
||||
24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311,
|
||||
23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153,
|
||||
22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942,
|
||||
20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680,
|
||||
19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371,
|
||||
18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017,
|
||||
16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623,
|
||||
15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191,
|
||||
14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724,
|
||||
12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227,
|
||||
11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703,
|
||||
9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156,
|
||||
7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589,
|
||||
6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006,
|
||||
4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411,
|
||||
3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808,
|
||||
1607, 1406, 1206, 1005, 804, 603, 402, 201,
|
||||
0, -201, -402, -603, -804, -1005, -1206, -1406,
|
||||
-1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011,
|
||||
-3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608,
|
||||
-4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195,
|
||||
-6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766,
|
||||
-7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319,
|
||||
-9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849,
|
||||
-11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353,
|
||||
-12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827,
|
||||
-14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268,
|
||||
-15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672,
|
||||
-16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036,
|
||||
-18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357,
|
||||
-19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631,
|
||||
-20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855,
|
||||
-22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027,
|
||||
-23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143,
|
||||
-24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201,
|
||||
-25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198,
|
||||
-26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132,
|
||||
-27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001,
|
||||
-28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802,
|
||||
-28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534,
|
||||
-29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195,
|
||||
-30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783,
|
||||
-30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297,
|
||||
-31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735,
|
||||
-31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097,
|
||||
-32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382,
|
||||
-32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588,
|
||||
-32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717,
|
||||
-32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766,
|
||||
-32767, -32766, -32764, -32761, -32757, -32751, -32744, -32736,
|
||||
-32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628,
|
||||
-32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441,
|
||||
-32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176,
|
||||
-32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833,
|
||||
-31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413,
|
||||
-31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918,
|
||||
-30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349,
|
||||
-30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706,
|
||||
-29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992,
|
||||
-28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208,
|
||||
-28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355,
|
||||
-27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437,
|
||||
-26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456,
|
||||
-25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413,
|
||||
-24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311,
|
||||
-23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153,
|
||||
-22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942,
|
||||
-20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680,
|
||||
-19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371,
|
||||
-18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017,
|
||||
-16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623,
|
||||
-15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191,
|
||||
-14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724,
|
||||
-12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227,
|
||||
-11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703,
|
||||
-9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156,
|
||||
-7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589,
|
||||
-6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006,
|
||||
-4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411,
|
||||
-3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808,
|
||||
-1607, -1406, -1206, -1005, -804, -603, -402, -201,
|
||||
};
|
||||
|
||||
#if N_LOUD != 100
|
||||
ERROR: N_LOUD != 100
|
||||
#endif
|
||||
fixed Loudampl[100] = {
|
||||
32767, 29203, 26027, 23197, 20674, 18426, 16422, 14636,
|
||||
13044, 11626, 10361, 9234, 8230, 7335, 6537, 5826,
|
||||
5193, 4628, 4125, 3676, 3276, 2920, 2602, 2319,
|
||||
2067, 1842, 1642, 1463, 1304, 1162, 1036, 923,
|
||||
823, 733, 653, 582, 519, 462, 412, 367,
|
||||
327, 292, 260, 231, 206, 184, 164, 146,
|
||||
130, 116, 103, 92, 82, 73, 65, 58,
|
||||
51, 46, 41, 36, 32, 29, 26, 23,
|
||||
20, 18, 16, 14, 13, 11, 10, 9,
|
||||
8, 7, 6, 5, 5, 4, 4, 3,
|
||||
3, 2, 2, 2, 2, 1, 1, 1,
|
||||
1, 1, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
};
|
||||
|
||||
#ifdef MAIN
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#define M 4
|
||||
#define N (1<<M)
|
||||
|
||||
main(){
|
||||
fixed real[N], imag[N];
|
||||
int i;
|
||||
|
||||
for (i=0; i<N; i++){
|
||||
real[i] = 1000*cos(i*2*3.1415926535/N);
|
||||
imag[i] = 0;
|
||||
}
|
||||
|
||||
fix_fft(real, imag, M, 0;
|
||||
|
||||
for (i=0; i<N; i++){
|
||||
printf("%d: %d, %d\n", i, real[i], imag[i]);
|
||||
}
|
||||
|
||||
fix_fft(real, imag, M, 1);
|
||||
|
||||
for (i=0; i<N; i++){
|
||||
printf("%d: %d, %d\n", i, real[i], imag[i]);
|
||||
}
|
||||
}
|
||||
#endif /* MAIN */
|
||||
|
||||
|
1372
src/driver/audiodec/mp3dec.cpp
Normal file
1372
src/driver/audiodec/mp3dec.cpp
Normal file
File diff suppressed because it is too large
Load Diff
81
src/driver/audiodec/mp3dec.h
Normal file
81
src/driver/audiodec/mp3dec.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2002 Bjoern Kalkbrenner <terminar@cyberphoria.org>
|
||||
Copyright (C) 2002,2003 Dirch
|
||||
Copyright (C) 2002,2003,2004 Zwen
|
||||
|
||||
libmad MP3 low-level core
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
based on
|
||||
************************************
|
||||
*** madlld -- Mad low-level *** v 1.0p1, 2002-01-08
|
||||
*** demonstration/decoder *** (c) 2001, 2002 Bertrand Petit
|
||||
************************************
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __MP3_DEC__
|
||||
#define __MP3_DEC__
|
||||
|
||||
#include <mad.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <basedec.h>
|
||||
#include <driver/audiometadata.h>
|
||||
extern "C"
|
||||
{
|
||||
#include "driver/audiodec/tag.h"
|
||||
}
|
||||
|
||||
class CMP3Dec : public CBaseDec
|
||||
{
|
||||
private:
|
||||
#if !((MAD_VERSION_MAJOR>=1) || \
|
||||
((MAD_VERSION_MAJOR==0) && \
|
||||
(((MAD_VERSION_MINOR==14) && \
|
||||
(MAD_VERSION_PATCH>=2)) || \
|
||||
(MAD_VERSION_MINOR>14))))
|
||||
const char* MadErrorString(const struct mad_stream *Stream);
|
||||
#endif
|
||||
signed short MadFixedToSShort(const mad_fixed_t Fixed, bool left = false);
|
||||
void CreateInfo(CAudioMetaData* const, const int);
|
||||
bool GetMP3Info(FILE*, const bool, CAudioMetaData* const);
|
||||
void GetID3(FILE*, CAudioMetaData* const);
|
||||
long scanHeader( FILE*, struct mad_header* const, struct tag* const,
|
||||
const bool );
|
||||
|
||||
public:
|
||||
static CMP3Dec* getInstance();
|
||||
virtual RetCode Decoder(FILE *InputFp, const int OutputFd,
|
||||
State* const state, CAudioMetaData* m,
|
||||
time_t* const t, unsigned int* const secondsToSkip);
|
||||
bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* const m);
|
||||
CMP3Dec(){};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
348
src/driver/audiodec/oggdec.cpp
Normal file
348
src/driver/audiodec/oggdec.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2004 Sania, Zwen
|
||||
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
ogg vorbis audio decoder
|
||||
uses tremor libvorbisidec
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <oggdec.h>
|
||||
#include <linux/soundcard.h>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <driver/netfile.h>
|
||||
|
||||
#include <audio_cs.h>
|
||||
|
||||
extern cAudio * audioDecoder;
|
||||
|
||||
#define ProgName "OggDec"
|
||||
// nr of msecs to skip in ff/rev mode
|
||||
#define MSECS_TO_SKIP 3000
|
||||
// nr of msecs to play in ff/rev mode
|
||||
#define MSECS_TO_PLAY 200
|
||||
|
||||
/* at first, define our own callback functions used in */
|
||||
/* tremor to access the data. These functions are simple mappers */
|
||||
size_t ogg_read(void *buf, size_t size, size_t nmemb, void *data)
|
||||
{
|
||||
return fread(buf, size, nmemb, (FILE*)data);
|
||||
}
|
||||
|
||||
int ogg_seek(void *data, ogg_int64_t offset, int whence)
|
||||
{
|
||||
return fseek((FILE*)data, (long)offset, whence);
|
||||
}
|
||||
|
||||
int ogg_close(void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
long ogg_tell(void *data)
|
||||
{
|
||||
return ftell((FILE*)data);
|
||||
}
|
||||
|
||||
#define PCMBUFFER 4096 //4096 max for libtremor
|
||||
#define MAX_OUTPUT_SAMPLES 2048 /* AVIA_GT_PCM_MAX_SAMPLES-1 */
|
||||
|
||||
CBaseDec::RetCode COggDec::Decoder(FILE *in, const int OutputFd, State* const state, CAudioMetaData* meta_data, time_t* const time_played, unsigned int* const secondsToSkip)
|
||||
{
|
||||
OggVorbis_File vf;
|
||||
int bitstream, rval;
|
||||
ogg_int64_t jumptime=0;
|
||||
Status=OK;
|
||||
mOutputFd = OutputFd;
|
||||
mState = state;
|
||||
mTimePlayed=time_played;
|
||||
|
||||
if (!Open(in, &vf))
|
||||
{
|
||||
Status=DATA_ERR;
|
||||
return Status;
|
||||
}
|
||||
|
||||
SetMetaData(&vf, meta_data);
|
||||
|
||||
audioDecoder->PrepareClipPlay(ov_info(&vf,0)->channels, ov_info(&vf,0)->rate, 16, 1);
|
||||
|
||||
/* up and away ... */
|
||||
mSlotSize = MAX_OUTPUT_SAMPLES * 2 * ov_info(&vf,0)->channels;
|
||||
for(int i = 0 ; i < DECODE_SLOTS ; i++)
|
||||
{
|
||||
if ((mPcmSlots[i] = (char*) malloc(mSlotSize)) == NULL)
|
||||
{
|
||||
for (int j = i - 1; j >= 0; j--)
|
||||
{
|
||||
free(mPcmSlots[j]);
|
||||
}
|
||||
Status=INTERNAL_ERR;
|
||||
return Status;
|
||||
}
|
||||
mSlotTime[i]=0;
|
||||
}
|
||||
mReadSlot=mWriteSlot=0;
|
||||
|
||||
pthread_t OutputThread;
|
||||
if (pthread_create (&OutputThread, 0, OutputDsp, (void *) this) != 0 )
|
||||
{
|
||||
for(int i = 0 ; i < DECODE_SLOTS ; i++)
|
||||
free(mPcmSlots[i]);
|
||||
Status=INTERNAL_ERR;
|
||||
return Status;
|
||||
}
|
||||
|
||||
int bytes;
|
||||
State oldstate=*state;
|
||||
do
|
||||
{
|
||||
// clear buffer on state change
|
||||
if(oldstate!=*state)
|
||||
{
|
||||
if(*state!=PAUSE && (*state!=PLAY || oldstate!=PAUSE))
|
||||
{
|
||||
mWriteSlot=mReadSlot=0;
|
||||
oldstate=*state;
|
||||
}
|
||||
}
|
||||
while((mWriteSlot+1)%DECODE_SLOTS == mReadSlot)
|
||||
{
|
||||
usleep(10000);
|
||||
}
|
||||
bytes=0;
|
||||
if(mSeekable)
|
||||
#ifdef DBOX
|
||||
mSlotTime[mWriteSlot] = ov_time_tell(&vf);
|
||||
#else
|
||||
mSlotTime[mWriteSlot] = (ogg_int64_t)(1000 * ov_time_tell(&vf));
|
||||
#endif
|
||||
do
|
||||
{
|
||||
#ifdef DBOX
|
||||
rval = ov_read(&vf, mPcmSlots[mWriteSlot]+bytes, mSlotSize-bytes, &bitstream);
|
||||
#else
|
||||
rval = ov_read(&vf, mPcmSlots[mWriteSlot]+bytes, mSlotSize-bytes, 0, 2, 1, &bitstream);
|
||||
#endif
|
||||
bytes+=rval;
|
||||
//printf("Ogg: read buf 0x%x size %d / %d done %d\n", mPcmSlots[mWriteSlot]+bytes, rval, mSlotSize, bytes);
|
||||
} while (rval > 0 && bytes !=mSlotSize);
|
||||
//printf("\n");
|
||||
int actMSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip * 1000 : MSECS_TO_SKIP;
|
||||
if((*state==FF || *state==REV) && mSeekable )
|
||||
{
|
||||
if((std::abs((long int)( mSlotTime[mWriteSlot]-jumptime))) > MSECS_TO_PLAY)
|
||||
{
|
||||
if(*state==FF)
|
||||
{
|
||||
ov_time_seek_page(&vf, mSlotTime[mWriteSlot] + actMSecsToSkip);
|
||||
jumptime=mSlotTime[mWriteSlot]+actMSecsToSkip;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mSlotTime[mWriteSlot] < actMSecsToSkip)
|
||||
{
|
||||
ov_time_seek(&vf, 0);
|
||||
*state=PLAY;
|
||||
}
|
||||
else
|
||||
{
|
||||
ov_time_seek_page(&vf, mSlotTime[mWriteSlot] - actMSecsToSkip);
|
||||
jumptime=mSlotTime[mWriteSlot]-actMSecsToSkip;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*secondsToSkip != 0) {
|
||||
*state=PLAY;
|
||||
}
|
||||
}
|
||||
if(bytes == mSlotSize)
|
||||
mWriteSlot=(mWriteSlot+1) % DECODE_SLOTS;
|
||||
} while (rval != 0 && *state!=STOP_REQ && Status==OK);
|
||||
|
||||
//printf("COggDec::Decoder: read loop stop, rval %d state %d status %d\n", rval, *state, Status);
|
||||
// let buffer run dry
|
||||
while(rval==0 && *state!=STOP_REQ && Status==OK && mReadSlot != mWriteSlot)
|
||||
usleep(100000);
|
||||
|
||||
//pthread_cancel(OutputThread);
|
||||
//printf("COggDec::Decoder: OutputThread join\n");
|
||||
Status = WRITE_ERR;
|
||||
pthread_join(OutputThread, NULL);
|
||||
//printf("COggDec::Decoder: OutputThread join done\n");
|
||||
audioDecoder->StopClip();
|
||||
for(int i = 0 ; i < DECODE_SLOTS ; i++)
|
||||
free(mPcmSlots[i]);
|
||||
|
||||
/* clean up the junk from the party */
|
||||
ov_clear(&vf);
|
||||
|
||||
/* and drive home ;) */
|
||||
return Status;
|
||||
}
|
||||
|
||||
void* COggDec::OutputDsp(void * arg)
|
||||
{
|
||||
COggDec* dec = (COggDec*) arg;
|
||||
while(dec->Status == OK/*true*/)
|
||||
{
|
||||
while(dec->mReadSlot==dec->mWriteSlot || *dec->mState==PAUSE)
|
||||
{
|
||||
if(dec->Status != OK)
|
||||
return NULL;
|
||||
usleep(10000);
|
||||
}
|
||||
//if (write(dec->mOutputFd, dec->mPcmSlots[dec->mReadSlot], dec->mSlotSize) != dec->mSlotSize)
|
||||
if (audioDecoder->WriteClip((unsigned char *)dec->mPcmSlots[dec->mReadSlot], dec->mSlotSize) != dec->mSlotSize)
|
||||
{
|
||||
fprintf(stderr,"%s: PCM write error (%s).\n", ProgName, strerror(errno));
|
||||
dec->Status=WRITE_ERR;
|
||||
break;
|
||||
}
|
||||
*dec->mTimePlayed = (int)(dec->mSlotTime[dec->mReadSlot]/1000);
|
||||
dec->mReadSlot=(dec->mReadSlot+1)%DECODE_SLOTS;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool COggDec::GetMetaData(FILE *in, const bool nice, CAudioMetaData* m)
|
||||
{
|
||||
OggVorbis_File vf;
|
||||
if (!Open(in, &vf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SetMetaData(&vf, m);
|
||||
ov_clear(&vf);
|
||||
return true;
|
||||
}
|
||||
|
||||
COggDec* COggDec::getInstance()
|
||||
{
|
||||
static COggDec* OggDec = NULL;
|
||||
if(OggDec == NULL)
|
||||
{
|
||||
OggDec = new COggDec();
|
||||
}
|
||||
return OggDec;
|
||||
}
|
||||
|
||||
void COggDec::ParseUserComments(vorbis_comment* vc, CAudioMetaData* m)
|
||||
{
|
||||
for(int i=0; i < vc->comments ; i++)
|
||||
{
|
||||
char* search;
|
||||
if((search=strstr(vc->user_comments[i],"Artist"))!=NULL ||
|
||||
(search=strstr(vc->user_comments[i],"ARTIST"))!=NULL)
|
||||
m->artist = search+7;
|
||||
else if((search=strstr(vc->user_comments[i],"Album"))!=NULL ||
|
||||
(search=strstr(vc->user_comments[i],"ALBUM"))!=NULL)
|
||||
m->album = search+6;
|
||||
else if((search=strstr(vc->user_comments[i],"Title"))!=NULL ||
|
||||
(search=strstr(vc->user_comments[i],"TITLE"))!=NULL)
|
||||
m->title = search+6;
|
||||
else if((search=strstr(vc->user_comments[i],"Genre"))!=NULL ||
|
||||
(search=strstr(vc->user_comments[i],"GENRE"))!=NULL)
|
||||
m->genre = search+6;
|
||||
else if((search=strstr(vc->user_comments[i],"Date"))!=NULL ||
|
||||
(search=strstr(vc->user_comments[i],"DATE"))!=NULL)
|
||||
m->date = search+5;
|
||||
else if((search=strstr(vc->user_comments[i],"TrackNumber"))!=NULL ||
|
||||
(search=strstr(vc->user_comments[i],"TRACKNUMBER"))!=NULL)
|
||||
m->track = search+12;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void COggDec::SetMetaData(OggVorbis_File* vf, CAudioMetaData* m)
|
||||
{
|
||||
/* Set Metadata */
|
||||
m->type = CAudioMetaData::OGG;
|
||||
m->bitrate = ov_info(vf,0)->bitrate_nominal;
|
||||
m->samplerate = ov_info(vf,0)->rate;
|
||||
if(mSeekable)
|
||||
#ifdef DBOX
|
||||
m->total_time = (time_t) ov_time_total(vf, 0) / 1000;
|
||||
#else
|
||||
m->total_time = (time_t) ov_time_total(vf, 0);
|
||||
#endif
|
||||
std::stringstream ss;
|
||||
ss << "OGG V." << ov_info(vf,0)->version << " / " << ov_info(vf,0)->channels << "channel(s)";
|
||||
m->type_info = ss.str();
|
||||
ParseUserComments(ov_comment(vf, 0), m);
|
||||
m->changed=true;
|
||||
}
|
||||
|
||||
bool COggDec::Open(FILE* in, OggVorbis_File* vf)
|
||||
{
|
||||
int rval;
|
||||
ov_callbacks cb;
|
||||
/* we need to use our own functions, because we have */
|
||||
/* the netfile layer hooked in here. If we would not */
|
||||
/* provide callbacks, the tremor lib and the netfile */
|
||||
/* layer would clash and steal each other the data */
|
||||
/* from the stream ! */
|
||||
|
||||
cb.read_func = ogg_read;
|
||||
cb.seek_func = ogg_seek;
|
||||
cb.close_func = ogg_close;
|
||||
cb.tell_func = ogg_tell;
|
||||
|
||||
/* test the dope ... */
|
||||
//rval = ov_test_callbacks((void*)in, vf, NULL, 0, cb);
|
||||
rval = ov_open_callbacks(in, vf, NULL, 0, cb);
|
||||
|
||||
/* and tell our friends about the quality of the stuff */
|
||||
// initialize the sound device here
|
||||
|
||||
if(rval<0)
|
||||
{
|
||||
switch(rval)
|
||||
{
|
||||
/* err_txt from netfile.cpp */
|
||||
case OV_EREAD: sprintf(err_txt, "media read error"); break;
|
||||
case OV_ENOTVORBIS: sprintf(err_txt, "no vorbis stream"); break;
|
||||
case OV_EVERSION: sprintf(err_txt, "incompatible vorbis version"); break;
|
||||
case OV_EBADHEADER: sprintf(err_txt, "invalid bvorbis bitstream header"); break;
|
||||
case OV_EFAULT: sprintf(err_txt, "internal logic fault (tremor)"); break;
|
||||
default: sprintf(err_txt, "unknown error, code: %d", rval);
|
||||
}
|
||||
fprintf(stderr,"%s: %s\n", ProgName, err_txt);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* finish the opening and ignite the joint */
|
||||
//ov_test_open(vf);
|
||||
|
||||
if(ov_seekable(vf))
|
||||
mSeekable = true;
|
||||
else
|
||||
mSeekable = false;
|
||||
|
||||
return true;
|
||||
}
|
78
src/driver/audiodec/oggdec.h
Normal file
78
src/driver/audiodec/oggdec.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2002,2003,2004 Sania,Zwen
|
||||
|
||||
ogg vorbis audio decoder
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __OGG_DEC__
|
||||
#define __OGG_DEC__
|
||||
|
||||
//#define DBOX
|
||||
|
||||
#include <mad.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <basedec.h>
|
||||
#include <driver/audiometadata.h>
|
||||
#ifdef DBOX
|
||||
#include <tremor/ogg.h>
|
||||
#include <tremor/ivorbisfile.h>
|
||||
#else
|
||||
#include <ogg/ogg.h>
|
||||
#include <vorbis/vorbisfile.h>
|
||||
#endif
|
||||
|
||||
#define DECODE_SLOTS 30
|
||||
|
||||
class COggDec : public CBaseDec
|
||||
{
|
||||
|
||||
public:
|
||||
static COggDec* getInstance();
|
||||
virtual RetCode Decoder(FILE *, const int, State* const, CAudioMetaData*, time_t* const, unsigned int* const);
|
||||
bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m);
|
||||
COggDec(){};
|
||||
private:
|
||||
void ParseUserComments(vorbis_comment*, CAudioMetaData*);
|
||||
bool Open(FILE* , OggVorbis_File*);
|
||||
void SetMetaData(OggVorbis_File*, CAudioMetaData*);
|
||||
RetCode Status;
|
||||
char* mPcmSlots[DECODE_SLOTS];
|
||||
ogg_int64_t mSlotTime[DECODE_SLOTS];
|
||||
int mWriteSlot;
|
||||
int mReadSlot;
|
||||
int mSlotSize;
|
||||
int mOutputFd;
|
||||
State* mState;
|
||||
bool mSeekable;
|
||||
time_t* mTimePlayed;
|
||||
static void* OutputDsp(void *);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
331
src/driver/audiodec/tag.c
Normal file
331
src/driver/audiodec/tag.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* madplay - MPEG audio decoder and player
|
||||
* Copyright (C) 2000-2004 Robert Leslie
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: tag.c,v 1.1 2004/08/28 14:11:58 rasc Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
//# include "global.h"
|
||||
|
||||
# include <mad.h>
|
||||
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
# include <driver/audiodec/crc.h>
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
# include <driver/audiodec/tag.h>
|
||||
|
||||
# define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
|
||||
# define INFO_MAGIC (('I' << 24) | ('n' << 16) | ('f' << 8) | 'o')
|
||||
# define LAME_MAGIC (('L' << 24) | ('A' << 16) | ('M' << 8) | 'E')
|
||||
|
||||
/*
|
||||
* NAME: tag->init()
|
||||
* DESCRIPTION: initialize tag structure
|
||||
*/
|
||||
void tag_init(struct tag *tag)
|
||||
{
|
||||
tag->flags = 0;
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
tag->encoder[0] = 0;
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: parse_xing()
|
||||
* DESCRIPTION: parse a Xing VBR tag
|
||||
*/
|
||||
static
|
||||
int parse_xing(struct tag_xing *xing,
|
||||
struct mad_bitptr *ptr, unsigned int *bitlen)
|
||||
{
|
||||
if (*bitlen < 32)
|
||||
goto fail;
|
||||
|
||||
xing->flags = mad_bit_read(ptr, 32);
|
||||
*bitlen -= 32;
|
||||
|
||||
if (xing->flags & TAG_XING_FRAMES) {
|
||||
if (*bitlen < 32)
|
||||
goto fail;
|
||||
|
||||
xing->frames = mad_bit_read(ptr, 32);
|
||||
*bitlen -= 32;
|
||||
}
|
||||
|
||||
if (xing->flags & TAG_XING_BYTES) {
|
||||
if (*bitlen < 32)
|
||||
goto fail;
|
||||
|
||||
xing->bytes = mad_bit_read(ptr, 32);
|
||||
*bitlen -= 32;
|
||||
}
|
||||
|
||||
if (xing->flags & TAG_XING_TOC) {
|
||||
int i;
|
||||
|
||||
if (*bitlen < 800)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < 100; ++i)
|
||||
xing->toc[i] = mad_bit_read(ptr, 8);
|
||||
|
||||
*bitlen -= 800;
|
||||
}
|
||||
|
||||
if (xing->flags & TAG_XING_SCALE) {
|
||||
if (*bitlen < 32)
|
||||
goto fail;
|
||||
|
||||
xing->scale = mad_bit_read(ptr, 32);
|
||||
*bitlen -= 32;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
xing->flags = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
/*
|
||||
* NAME: tag->parse_rgain()
|
||||
* DESCRIPTION: parse a 16-bit Replay Gain field
|
||||
*/
|
||||
void tag_parse_rgain(struct tag_rgain *rgain, struct mad_bitptr *ptr)
|
||||
{
|
||||
int negative;
|
||||
|
||||
rgain->name = mad_bit_read(ptr, 3);
|
||||
rgain->originator = mad_bit_read(ptr, 3);
|
||||
|
||||
negative = mad_bit_read(ptr, 1);
|
||||
rgain->adjustment = mad_bit_read(ptr, 9);
|
||||
|
||||
if (negative)
|
||||
rgain->adjustment = -rgain->adjustment;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: parse_lame()
|
||||
* DESCRIPTION: parse a LAME tag
|
||||
*/
|
||||
static
|
||||
int parse_lame(struct tag_lame *lame,
|
||||
struct mad_bitptr *ptr, unsigned int *bitlen,
|
||||
unsigned short crc)
|
||||
{
|
||||
struct mad_bitptr save = *ptr;
|
||||
|
||||
if (*bitlen < 36 * 8)
|
||||
goto fail;
|
||||
|
||||
/* bytes $9A-$A4: Encoder short VersionString */
|
||||
|
||||
mad_bit_skip(ptr, 9 * 8);
|
||||
|
||||
/* byte $A5: Info Tag revision + VBR method */
|
||||
|
||||
lame->revision = mad_bit_read(ptr, 4);
|
||||
if (lame->revision == 15)
|
||||
goto fail;
|
||||
|
||||
lame->vbr_method = mad_bit_read(ptr, 4);
|
||||
|
||||
/* byte $A6: Lowpass filter value (Hz) */
|
||||
|
||||
lame->lowpass_filter = mad_bit_read(ptr, 8) * 100;
|
||||
|
||||
/* bytes $A7-$AA: 32 bit "Peak signal amplitude" */
|
||||
|
||||
lame->peak = mad_bit_read(ptr, 32) << 5;
|
||||
|
||||
/* bytes $AB-$AC: 16 bit "Radio Replay Gain" */
|
||||
|
||||
tag_parse_rgain(&lame->replay_gain[0], ptr);
|
||||
|
||||
/* bytes $AD-$AE: 16 bit "Audiophile Replay Gain" */
|
||||
|
||||
tag_parse_rgain(&lame->replay_gain[1], ptr);
|
||||
|
||||
/* byte $AF: Encoding flags + ATH Type */
|
||||
|
||||
lame->flags = mad_bit_read(ptr, 4);
|
||||
lame->ath_type = mad_bit_read(ptr, 4);
|
||||
|
||||
/* byte $B0: if ABR {specified bitrate} else {minimal bitrate} */
|
||||
|
||||
lame->bitrate = mad_bit_read(ptr, 8);
|
||||
|
||||
/* bytes $B1-$B3: Encoder delays */
|
||||
|
||||
lame->start_delay = mad_bit_read(ptr, 12);
|
||||
lame->end_padding = mad_bit_read(ptr, 12);
|
||||
|
||||
/* byte $B4: Misc */
|
||||
|
||||
lame->source_samplerate = mad_bit_read(ptr, 2);
|
||||
|
||||
if (mad_bit_read(ptr, 1))
|
||||
lame->flags |= TAG_LAME_UNWISE;
|
||||
|
||||
lame->stereo_mode = mad_bit_read(ptr, 3);
|
||||
lame->noise_shaping = mad_bit_read(ptr, 2);
|
||||
|
||||
/* byte $B5: MP3 Gain */
|
||||
|
||||
lame->gain = mad_bit_read(ptr, 8);
|
||||
|
||||
/* bytes $B6-B7: Preset and surround info */
|
||||
|
||||
mad_bit_skip(ptr, 2);
|
||||
|
||||
lame->surround = mad_bit_read(ptr, 3);
|
||||
lame->preset = mad_bit_read(ptr, 11);
|
||||
|
||||
/* bytes $B8-$BB: MusicLength */
|
||||
|
||||
lame->music_length = mad_bit_read(ptr, 32);
|
||||
|
||||
/* bytes $BC-$BD: MusicCRC */
|
||||
|
||||
lame->music_crc = mad_bit_read(ptr, 16);
|
||||
|
||||
/* bytes $BE-$BF: CRC-16 of Info Tag */
|
||||
|
||||
if (mad_bit_read(ptr, 16) != crc)
|
||||
goto fail;
|
||||
|
||||
*bitlen -= 36 * 8;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*ptr = save;
|
||||
return -1;
|
||||
}
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
|
||||
/*
|
||||
* NAME: tag->parse()
|
||||
* DESCRIPTION: parse Xing/LAME tag(s)
|
||||
*/
|
||||
int tag_parse(struct tag *tag, struct mad_stream const *stream)
|
||||
{
|
||||
struct mad_bitptr ptr = stream->anc_ptr;
|
||||
struct mad_bitptr start = ptr;
|
||||
unsigned int bitlen = stream->anc_bitlen;
|
||||
unsigned long magic;
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
int i;
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
|
||||
if (bitlen < 32)
|
||||
return -1;
|
||||
|
||||
magic = mad_bit_read(&ptr, 32);
|
||||
bitlen -= 32;
|
||||
|
||||
if (magic != XING_MAGIC
|
||||
&& magic != INFO_MAGIC
|
||||
&& magic != LAME_MAGIC)
|
||||
{
|
||||
/*
|
||||
* Due to an unfortunate historical accident, a Xing VBR tag may be
|
||||
* misplaced in a stream with CRC protection. We check for this by
|
||||
* assuming the tag began two octets prior and the high bits of the
|
||||
* following flags field are always zero.
|
||||
*/
|
||||
|
||||
if (magic != ((XING_MAGIC << 16) & 0xffffffffL)
|
||||
&& magic != ((INFO_MAGIC << 16) & 0xffffffffL))
|
||||
return -1;
|
||||
|
||||
magic >>= 16;
|
||||
|
||||
/* backtrack the bit pointer */
|
||||
|
||||
ptr = start;
|
||||
mad_bit_skip(&ptr, 16);
|
||||
bitlen += 16;
|
||||
}
|
||||
|
||||
if ((magic & 0x0000ffffL) == (XING_MAGIC & 0x0000ffffL))
|
||||
tag->flags |= TAG_VBR;
|
||||
|
||||
/* Xing tag */
|
||||
|
||||
if (magic == LAME_MAGIC) {
|
||||
ptr = start;
|
||||
bitlen += 32;
|
||||
}
|
||||
else if (parse_xing(&tag->xing, &ptr, &bitlen) == 0)
|
||||
tag->flags |= TAG_XING;
|
||||
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
/* encoder string */
|
||||
|
||||
if (bitlen >= 20 * 8) {
|
||||
start = ptr;
|
||||
|
||||
for (i = 0; i < 20; ++i) {
|
||||
tag->encoder[i] = mad_bit_read(&ptr, 8);
|
||||
|
||||
if (tag->encoder[i] == 0)
|
||||
break;
|
||||
|
||||
/* keep only printable ASCII chars */
|
||||
|
||||
if (tag->encoder[i] < 0x20 || tag->encoder[i] >= 0x7f) {
|
||||
tag->encoder[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tag->encoder[20] = 0;
|
||||
ptr = start;
|
||||
}
|
||||
|
||||
/* LAME tag */
|
||||
|
||||
if (stream->next_frame - stream->this_frame >= 192 &&
|
||||
parse_lame(&tag->lame, &ptr, &bitlen,
|
||||
crc_compute(stream->this_frame, 190, 0x0000)) == 0) {
|
||||
tag->flags |= TAG_LAME;
|
||||
tag->encoder[9] = 0;
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < 20; ++i) {
|
||||
if (tag->encoder[i] == 0)
|
||||
break;
|
||||
|
||||
/* stop at padding chars */
|
||||
|
||||
if (tag->encoder[i] == 0x55) {
|
||||
tag->encoder[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
|
||||
return 0;
|
||||
}
|
183
src/driver/audiodec/tag.h
Normal file
183
src/driver/audiodec/tag.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* madplay - MPEG audio decoder and player
|
||||
* Copyright (C) 2000-2004 Robert Leslie
|
||||
*
|
||||
* 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 program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Id: tag.h,v 1.1 2004/08/28 14:11:58 rasc Exp $
|
||||
*/
|
||||
|
||||
# ifndef __TAG_H__
|
||||
# define __TAG_H__
|
||||
|
||||
# include <mad.h>
|
||||
|
||||
enum {
|
||||
TAG_XING = 0x0001,
|
||||
TAG_LAME = 0x0002,
|
||||
TAG_VBR = 0x0100
|
||||
};
|
||||
|
||||
enum {
|
||||
TAG_XING_FRAMES = 0x00000001L,
|
||||
TAG_XING_BYTES = 0x00000002L,
|
||||
TAG_XING_TOC = 0x00000004L,
|
||||
TAG_XING_SCALE = 0x00000008L
|
||||
};
|
||||
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
enum {
|
||||
TAG_LAME_NSPSYTUNE = 0x01,
|
||||
TAG_LAME_NSSAFEJOINT = 0x02,
|
||||
TAG_LAME_NOGAP_NEXT = 0x04,
|
||||
TAG_LAME_NOGAP_PREV = 0x08,
|
||||
TAG_LAME_UNWISE = 0x10
|
||||
};
|
||||
|
||||
enum tag_lame_vbr {
|
||||
TAG_LAME_VBR_CONSTANT = 1,
|
||||
TAG_LAME_VBR_ABR = 2,
|
||||
TAG_LAME_VBR_METHOD1 = 3,
|
||||
TAG_LAME_VBR_METHOD2 = 4,
|
||||
TAG_LAME_VBR_METHOD3 = 5,
|
||||
TAG_LAME_VBR_METHOD4 = 6,
|
||||
TAG_LAME_VBR_CONSTANT2PASS = 8,
|
||||
TAG_LAME_VBR_ABR2PASS = 9
|
||||
};
|
||||
|
||||
enum tag_lame_source {
|
||||
TAG_LAME_SOURCE_32LOWER = 0x00,
|
||||
TAG_LAME_SOURCE_44_1 = 0x01,
|
||||
TAG_LAME_SOURCE_48 = 0x02,
|
||||
TAG_LAME_SOURCE_HIGHER48 = 0x03
|
||||
};
|
||||
|
||||
enum tag_lame_mode {
|
||||
TAG_LAME_MODE_MONO = 0x00,
|
||||
TAG_LAME_MODE_STEREO = 0x01,
|
||||
TAG_LAME_MODE_DUAL = 0x02,
|
||||
TAG_LAME_MODE_JOINT = 0x03,
|
||||
TAG_LAME_MODE_FORCE = 0x04,
|
||||
TAG_LAME_MODE_AUTO = 0x05,
|
||||
TAG_LAME_MODE_INTENSITY = 0x06,
|
||||
TAG_LAME_MODE_UNDEFINED = 0x07
|
||||
};
|
||||
|
||||
enum tag_lame_surround {
|
||||
TAG_LAME_SURROUND_NONE = 0,
|
||||
TAG_LAME_SURROUND_DPL = 1,
|
||||
TAG_LAME_SURROUND_DPL2 = 2,
|
||||
TAG_LAME_SURROUND_AMBISONIC = 3
|
||||
};
|
||||
|
||||
enum tag_lame_preset {
|
||||
TAG_LAME_PRESET_NONE = 0,
|
||||
TAG_LAME_PRESET_V9 = 410,
|
||||
TAG_LAME_PRESET_V8 = 420,
|
||||
TAG_LAME_PRESET_V7 = 430,
|
||||
TAG_LAME_PRESET_V6 = 440,
|
||||
TAG_LAME_PRESET_V5 = 450,
|
||||
TAG_LAME_PRESET_V4 = 460,
|
||||
TAG_LAME_PRESET_V3 = 470,
|
||||
TAG_LAME_PRESET_V2 = 480,
|
||||
TAG_LAME_PRESET_V1 = 490,
|
||||
TAG_LAME_PRESET_V0 = 500,
|
||||
TAG_LAME_PRESET_R3MIX = 1000,
|
||||
TAG_LAME_PRESET_STANDARD = 1001,
|
||||
TAG_LAME_PRESET_EXTREME = 1002,
|
||||
TAG_LAME_PRESET_INSANE = 1003,
|
||||
TAG_LAME_PRESET_STANDARD_FAST = 1004,
|
||||
TAG_LAME_PRESET_EXTREME_FAST = 1005,
|
||||
TAG_LAME_PRESET_MEDIUM = 1006,
|
||||
TAG_LAME_PRESET_MEDIUM_FAST = 1007
|
||||
};
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
|
||||
struct tag_xing {
|
||||
long flags; /* valid fields (see below) */
|
||||
unsigned long frames; /* total number of frames */
|
||||
unsigned long bytes; /* total number of bytes */
|
||||
unsigned char toc[100]; /* 100-point seek table */
|
||||
long scale; /* VBR quality indicator (0 best - 100 worst) */
|
||||
};
|
||||
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
enum tag_rgain_name {
|
||||
TAG_RGAIN_NAME_NOT_SET = 0x0,
|
||||
TAG_RGAIN_NAME_RADIO = 0x1,
|
||||
TAG_RGAIN_NAME_AUDIOPHILE = 0x2
|
||||
};
|
||||
|
||||
enum tag_rgain_originator {
|
||||
TAG_RGAIN_ORIGINATOR_UNSPECIFIED = 0x0,
|
||||
TAG_RGAIN_ORIGINATOR_PRESET = 0x1,
|
||||
TAG_RGAIN_ORIGINATOR_USER = 0x2,
|
||||
TAG_RGAIN_ORIGINATOR_AUTOMATIC = 0x3
|
||||
};
|
||||
|
||||
struct tag_rgain {
|
||||
enum tag_rgain_name name;
|
||||
enum tag_rgain_originator originator;
|
||||
signed short adjustment;
|
||||
};
|
||||
|
||||
struct tag_lame {
|
||||
unsigned char revision;
|
||||
unsigned char flags;
|
||||
|
||||
enum tag_lame_vbr vbr_method;
|
||||
unsigned short lowpass_filter;
|
||||
|
||||
mad_fixed_t peak;
|
||||
struct tag_rgain replay_gain[2];
|
||||
|
||||
unsigned char ath_type;
|
||||
unsigned char bitrate;
|
||||
|
||||
unsigned short start_delay;
|
||||
unsigned short end_padding;
|
||||
|
||||
enum tag_lame_source source_samplerate;
|
||||
enum tag_lame_mode stereo_mode;
|
||||
unsigned char noise_shaping;
|
||||
|
||||
signed char gain;
|
||||
enum tag_lame_surround surround;
|
||||
enum tag_lame_preset preset;
|
||||
|
||||
unsigned long music_length;
|
||||
unsigned short music_crc;
|
||||
};
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
|
||||
struct tag {
|
||||
int flags;
|
||||
struct tag_xing xing;
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
struct tag_lame lame;
|
||||
char encoder[21];
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
};
|
||||
|
||||
void tag_init(struct tag *);
|
||||
|
||||
# define tag_finish(tag) /* nothing */
|
||||
|
||||
int tag_parse(struct tag *, struct mad_stream const *);
|
||||
#ifdef INCLUDE_UNUSED_STUFF
|
||||
void tag_parse_rgain(struct tag_rgain *, struct mad_bitptr *);
|
||||
#endif /* INCLUDE_UNUSED_STUFF */
|
||||
|
||||
# endif /* __TAG_H__ */
|
185
src/driver/audiodec/vis.cpp
Normal file
185
src/driver/audiodec/vis.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <global.h>
|
||||
#include <neutrino.h>
|
||||
#include <driver/vfd.h>
|
||||
|
||||
#include "int_fft.c"
|
||||
typedef signed short gint16;
|
||||
typedef int gint;
|
||||
typedef float gfloat;
|
||||
typedef double gdouble;
|
||||
|
||||
#define NUM_BANDS 16
|
||||
#define WIDTH (120/NUM_BANDS)
|
||||
#define HEIGHT 64
|
||||
#define FALL 2
|
||||
#define FALLOFF 1
|
||||
|
||||
static int bar_heights[NUM_BANDS];
|
||||
static int falloffs[NUM_BANDS];
|
||||
static gdouble scale = 0;
|
||||
|
||||
static gint xscale128[] = { 0, 1, 2, 3, 4, 6, 8, 9, 10, 14, 20, 27, 37, 50, 67, 94, 127 };
|
||||
static gint xscale256[] = { 0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 187, 255 };
|
||||
static gint xscale512[] = { 0, 2, 4, 6, 10, 14, 20, 28, 40, 56, 80, 108, 148, 202, 274, 374, 510 };
|
||||
|
||||
//#define DEBUG
|
||||
#if HAVE_DBOX2
|
||||
#define SAMPLES 256
|
||||
#define LOG 8
|
||||
#define xscale xscale128
|
||||
#else
|
||||
#define SAMPLES 512
|
||||
#define LOG 9
|
||||
#define xscale xscale256
|
||||
#endif
|
||||
|
||||
static void do_fft(gint16 out_data[], gint16 in_data[])
|
||||
{
|
||||
gint16 im[SAMPLES];
|
||||
|
||||
memset(im, 0, sizeof(im));
|
||||
window(in_data, SAMPLES);
|
||||
fix_fft(in_data, im, LOG, 0);
|
||||
fix_loud(out_data, in_data, im, SAMPLES/2, 0);
|
||||
}
|
||||
|
||||
static int threshold = -60;
|
||||
void sanalyzer_render_freq (gint16 in_data[])
|
||||
{
|
||||
#if 0
|
||||
gint i, c;
|
||||
gint y;
|
||||
gint16 freq_data[1024];
|
||||
static int fl = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
struct timeval tv1, tv2;
|
||||
gettimeofday (&tv1, NULL);
|
||||
#endif
|
||||
do_fft (freq_data, in_data);
|
||||
|
||||
CVFD::getInstance ()->Clear ();
|
||||
#ifdef DEBUG
|
||||
//gettimeofday (&tv1, NULL);
|
||||
#endif
|
||||
for (i = 0; i < NUM_BANDS; i++) {
|
||||
y = 0;
|
||||
|
||||
#if 0 // using max value
|
||||
for (c = xscale[i]; c < xscale[i + 1]; c++) {
|
||||
if(freq_data[c] > threshold) {
|
||||
int val = freq_data[c]-threshold;
|
||||
if (val > y)
|
||||
y = val;
|
||||
}
|
||||
}
|
||||
#else
|
||||
int val = 0;
|
||||
int cnt = 0;
|
||||
for (c = xscale[i]; c < xscale[i + 1]; c++) {
|
||||
if(freq_data[c] > threshold) {
|
||||
val += freq_data[c]-threshold;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if(cnt) y = val/cnt;
|
||||
#endif
|
||||
|
||||
if (y > HEIGHT - 1)
|
||||
y = HEIGHT - 1;
|
||||
|
||||
if (y > bar_heights[i])
|
||||
bar_heights[i] = y;
|
||||
else if (bar_heights[i] > FALL)
|
||||
bar_heights[i] -= FALL;
|
||||
else
|
||||
bar_heights[i] = 0;
|
||||
|
||||
if (y > falloffs[i])
|
||||
falloffs[i] = y;
|
||||
else if (falloffs[i] > FALLOFF) {
|
||||
fl ++;
|
||||
if(fl > 2) {
|
||||
fl = 0;
|
||||
falloffs[i] -= FALLOFF;
|
||||
}
|
||||
}
|
||||
else
|
||||
falloffs[i] = 0;
|
||||
CVFD::getInstance ()->drawBar (i*WIDTH, 64-falloffs[i]-5, WIDTH, 2);
|
||||
y = bar_heights[i];
|
||||
CVFD::getInstance ()->drawBar (i*WIDTH, 64-y, WIDTH, y);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
gettimeofday (&tv2, NULL);
|
||||
printf("Calc takes %dns\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));
|
||||
#endif
|
||||
CVFD::getInstance ()->Update ();
|
||||
#endif
|
||||
}
|
||||
#if 0
|
||||
void sanalyzer_render_vu (gint16 in_data[2][512])
|
||||
{
|
||||
long delay_value;
|
||||
int a = 0,b = 0, c;
|
||||
float max = 32767.50;
|
||||
float db_min = (float) -91;
|
||||
float channel_data[2] = { 0.0, 0.0};
|
||||
static int olda = 0, oldb = 0;
|
||||
return;
|
||||
#ifdef DEBUG
|
||||
//struct timeval tv1, tv2;
|
||||
//gettimeofday (&tv1, NULL);
|
||||
#endif
|
||||
for (c = 0; c < 512; c += 2) {
|
||||
channel_data[0] += (float) (in_data[0][c] * in_data[0][c]);
|
||||
channel_data[1] += (float) (in_data[1][c] * in_data[1][c]);
|
||||
}
|
||||
channel_data[0] = 20 * log10 (sqrt (channel_data[0] / 256.0) / max);
|
||||
channel_data[1] = 20 * log10 (sqrt (channel_data[1] / 256.0) / max);
|
||||
|
||||
a = channel_data[0] + 91;
|
||||
b = channel_data[1] + 91;
|
||||
//printf ("channel0: %d\n", a);
|
||||
//printf ("channel1: %d\n", b);
|
||||
#ifdef DEBUG
|
||||
//gettimeofday (&tv2, NULL);
|
||||
//printf("Calc takes %dms\n", (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000);
|
||||
#endif
|
||||
if(a != olda || b != oldb) {
|
||||
CVFD::getInstance ()->Clear ();
|
||||
CVFD::getInstance ()->drawBar (0, 8, a, 20);
|
||||
CVFD::getInstance ()->drawBar (0, 36, b, 20);
|
||||
#ifdef DEBUG
|
||||
//gettimeofday (&tv1, NULL);
|
||||
#endif
|
||||
CVFD::getInstance ()->Update ();
|
||||
#ifdef DEBUG
|
||||
//gettimeofday (&tv2, NULL);
|
||||
//int ms = (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000;
|
||||
//if(ms) printf("Update takes %dms\n", ms);
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
213
src/driver/audiodec/wavdec.cpp
Normal file
213
src/driver/audiodec/wavdec.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2004 Zwen
|
||||
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
wav audio decoder
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <wavdec.h>
|
||||
#include <sstream>
|
||||
#include <driver/audioplay.h>
|
||||
#include <linux/soundcard.h>
|
||||
|
||||
#define ProgName "WavDec"
|
||||
// nr of msecs to skip in ff/rev mode
|
||||
#define MSECS_TO_SKIP 3000
|
||||
// nr of msecs to play in ff/rev mode
|
||||
#define MSECS_TO_PLAY 200
|
||||
|
||||
struct WavHeader
|
||||
{
|
||||
char ChunkID[4];
|
||||
int ChunkSize;
|
||||
char Format[4];
|
||||
char Subchunk1ID[4];
|
||||
int Subchunk1Size;
|
||||
short AudioFormat;
|
||||
short NumChannels;
|
||||
int SampleRate;
|
||||
int ByteRate;
|
||||
short BlockAlign;
|
||||
short BitsPerSample;
|
||||
char Subchunk2ID[4];
|
||||
int Subchunk2Size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
int endianTest=1;
|
||||
#define Swap32IfBE(l) \
|
||||
(*(char *)&endianTest ? (l) : \
|
||||
((((l) & 0xff000000) >> 24) | \
|
||||
(((l) & 0x00ff0000) >> 8) | \
|
||||
(((l) & 0x0000ff00) << 8) | \
|
||||
(((l) & 0x000000ff) << 24)))
|
||||
#define Swap16IfBE(l) \
|
||||
(*(char *)&endianTest ? (l) : \
|
||||
((((l) & 0xff00) >> 8) | \
|
||||
(((l) & 0x00ff) << 8)))
|
||||
|
||||
#define MAX_OUTPUT_SAMPLES 1022 /* AVIA_GT_PCM_MAX_SAMPLES-1 */
|
||||
|
||||
CBaseDec::RetCode CWavDec::Decoder(FILE *in, int OutputFd, State* state, CAudioMetaData* meta_data, time_t* time_played, unsigned int* secondsToSkip)
|
||||
{
|
||||
char* buffer;
|
||||
RetCode Status=OK;
|
||||
|
||||
if (!SetMetaData(in, meta_data))
|
||||
{
|
||||
Status=DATA_ERR;
|
||||
return Status;
|
||||
}
|
||||
fseek(in, header_size, SEEK_SET);
|
||||
int fmt;
|
||||
switch(mBitsPerSample)
|
||||
{
|
||||
case 8 : fmt = AFMT_U8;
|
||||
break;
|
||||
case 16 : fmt = header_size == 0 ? AFMT_S16_BE : AFMT_S16_LE;
|
||||
break;
|
||||
default:
|
||||
printf("%s: wrong bits per sample (%d)\n", ProgName, mBitsPerSample);
|
||||
Status=DATA_ERR;
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (SetDSP(OutputFd, fmt, meta_data->samplerate , mChannels))
|
||||
{
|
||||
Status=DSPSET_ERR;
|
||||
return Status;
|
||||
}
|
||||
int actSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip : MSECS_TO_SKIP / 1000;
|
||||
unsigned int oldSecsToSkip = *secondsToSkip;
|
||||
int jumppos=0;
|
||||
int bytes;
|
||||
int bytes_to_play = (int) (1.0 * MSECS_TO_PLAY / 1000 * meta_data->bitrate / 8);
|
||||
int bytes_to_skip = (int) (1.0 * actSecsToSkip * meta_data->bitrate / 8);
|
||||
int buffersize = MAX_OUTPUT_SAMPLES * mChannels * mBitsPerSample / 8;
|
||||
buffer = (char*) malloc (buffersize);
|
||||
do
|
||||
{
|
||||
while(*state==PAUSE)
|
||||
usleep(10000);
|
||||
|
||||
if(*state==FF || *state==REV)
|
||||
{
|
||||
if (oldSecsToSkip != *secondsToSkip)
|
||||
{
|
||||
actSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip : MSECS_TO_SKIP / 1000;
|
||||
bytes_to_skip = (int) (1.0 * actSecsToSkip * meta_data->bitrate / 8);
|
||||
oldSecsToSkip = *secondsToSkip;
|
||||
}
|
||||
//printf("skipping %d secs and %d bytes\n",actSecsToSkip,bytes_to_skip);
|
||||
if(std::abs(ftell(in)-jumppos) > bytes_to_play)
|
||||
{
|
||||
if(*state==FF)
|
||||
{
|
||||
fseek(in, bytes_to_skip, SEEK_CUR);
|
||||
jumppos=ftell(in);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ftell(in) < bytes_to_skip)
|
||||
{
|
||||
fseek(in, header_size, SEEK_SET);
|
||||
*state=PLAY;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(in, -bytes_to_skip, SEEK_CUR);
|
||||
jumppos=ftell(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if a custom value was set we only jump once
|
||||
if (*secondsToSkip != 0) {
|
||||
*state=PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
bytes = fread(buffer, 1, buffersize, in);
|
||||
if (write(OutputFd, buffer, bytes) != bytes)
|
||||
{
|
||||
fprintf(stderr,"%s: PCM write error (%s).\n", ProgName, strerror(errno));
|
||||
Status=WRITE_ERR;
|
||||
}
|
||||
*time_played = (meta_data->bitrate!=0) ? (ftell(in)-header_size)*8/meta_data->bitrate : 0;
|
||||
} while (bytes > 0 && *state!=STOP_REQ && Status==OK);
|
||||
free(buffer);
|
||||
return Status;
|
||||
}
|
||||
|
||||
bool CWavDec::GetMetaData(FILE *in, const bool nice, CAudioMetaData* m)
|
||||
{
|
||||
return SetMetaData(in, m);
|
||||
}
|
||||
|
||||
CWavDec* CWavDec::getInstance()
|
||||
{
|
||||
static CWavDec* WavDec = NULL;
|
||||
if(WavDec == NULL)
|
||||
{
|
||||
WavDec = new CWavDec();
|
||||
}
|
||||
return WavDec;
|
||||
}
|
||||
|
||||
bool CWavDec::SetMetaData(FILE* in, CAudioMetaData* m)
|
||||
{
|
||||
/* Set Metadata */
|
||||
struct WavHeader wh;
|
||||
|
||||
header_size = 44;
|
||||
|
||||
fseek(in, 0, SEEK_END);
|
||||
int filesize = ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
if(fread(&wh, sizeof(wh), 1, in)!=1)
|
||||
return false;
|
||||
if(memcmp(wh.ChunkID, "RIFF", 4)!=0 ||
|
||||
memcmp(wh.Format, "WAVE", 4)!=0 ||
|
||||
Swap16IfBE(wh.AudioFormat) != 1)
|
||||
{
|
||||
printf("%s: wrong format (header)\n", ProgName);
|
||||
return false;
|
||||
}
|
||||
m->type = CAudioMetaData::WAV;
|
||||
m->bitrate = Swap32IfBE(wh.ByteRate)*8;
|
||||
m->samplerate = Swap32IfBE(wh.SampleRate);
|
||||
mBitsPerSample = Swap16IfBE(wh.BitsPerSample);
|
||||
mChannels = Swap16IfBE(wh.NumChannels);
|
||||
m->total_time = (m->bitrate!=0) ? (filesize-header_size)*8 / m->bitrate : 0;
|
||||
std::stringstream ss;
|
||||
ss << "Riff/Wave / " << mChannels << "channel(s) / " << mBitsPerSample << "bit";
|
||||
m->type_info = ss.str();
|
||||
m->changed=true;
|
||||
return true;
|
||||
}
|
54
src/driver/audiodec/wavdec.h
Normal file
54
src/driver/audiodec/wavdec.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Neutrino-GUI - DBoxII-Project
|
||||
|
||||
Copyright (C) 2004 Zwen
|
||||
|
||||
wav audio decoder
|
||||
Homepage: http://www.dbox2.info/
|
||||
|
||||
Kommentar:
|
||||
|
||||
License: GPL
|
||||
|
||||
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 program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __WAV_DEC__
|
||||
#define __WAV_DEC__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <driver/audiodec/basedec.h>
|
||||
|
||||
class CWavDec : public CBaseDec
|
||||
{
|
||||
|
||||
public:
|
||||
static CWavDec* getInstance();
|
||||
virtual RetCode Decoder(FILE *,int , State* , CAudioMetaData* m, time_t* t, unsigned int* secondsToSkip);
|
||||
bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m);
|
||||
CWavDec(){};
|
||||
|
||||
protected:
|
||||
virtual bool SetMetaData(FILE* in, CAudioMetaData* m);
|
||||
|
||||
int mBitsPerSample;
|
||||
int mChannels;
|
||||
int header_size;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user