/* 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 #endif #include #include #include #include #include #include #include #include #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; }