Files
neutrino/src/driver/audiodec/wavdec.cpp
2012-05-08 15:49:36 +02:00

226 lines
5.8 KiB
C++

/*
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 <sstream>
#include <driver/audioplay.h>
#include <linux/soundcard.h>
#include <zapit/include/audio.h>
#include "wavdec.h"
extern cAudio * audioDecoder;
#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 2048 /* 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 0
if (SetDSP(OutputFd, fmt, meta_data->samplerate , mChannels))
{
Status=DSPSET_ERR;
return Status;
}
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
audioDecoder->PrepareClipPlay(mChannels, meta_data->samplerate, mBitsPerSample, 1);
#else
audioDecoder->PrepareClipPlay(mChannels, meta_data->samplerate, mBitsPerSample, 0);
#endif
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)
if(audioDecoder->WriteClip((unsigned char*) 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);
audioDecoder->StopClip();
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;
}