diff --git a/src/driver/audiodec/flacdec.cpp b/src/driver/audiodec/flacdec.cpp new file mode 100644 index 000000000..39ac27562 --- /dev/null +++ b/src/driver/audiodec/flacdec.cpp @@ -0,0 +1,657 @@ +/* + Neutrino-GUI - DBoxII-Project + + flac audio decoder + uses flac library + + License: GPL + + ported from tuxbox-CVS flacdec.cpp CVS rev 1.2 + + Neutrino-HD port and fixes for non-ppc architectures are: + (C) 2011 Stefan Seyfried, B1 Systems GmbH, Vohburg, Germany + + 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, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA +*/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +extern cAudio *audioDecoder; + +#define ProgName "FlacDec" +// 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 + +#define MAX_OUTPUT_SAMPLES 1022 /* AVIA_GT_PCM_MAX_SAMPLES-1 */ + +/* at first, define our own callback functions used in */ +/* tremor to access the data. These functions are simple mappers */ +FLAC__StreamDecoderReadStatus flac_read(const FLAC__StreamDecoder *, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + CFlacDec * flacdec = (CFlacDec *)client_data; + + if(*bytes > 0) { + *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, flacdec->mIn); + if(ferror(flacdec->mIn)) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + else if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; +} + +FLAC__StreamDecoderSeekStatus flac_seek(const FLAC__StreamDecoder *, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + CFlacDec * flacdec = (CFlacDec *)client_data; + + if(flacdec->mIn == stdin) + return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + else if(fseek(flacdec->mIn, (off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +int flac_close(void *) +{ + return 0; +} + +FLAC__StreamDecoderTellStatus flac_tell(const FLAC__StreamDecoder *, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + CFlacDec * flacdec = (CFlacDec *)client_data; + off_t pos; + + if(flacdec->mIn == stdin) + return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + else if((pos = ftell(flacdec->mIn)) < 0) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +FLAC__StreamDecoderLengthStatus flac_length(const FLAC__StreamDecoder *, FLAC__uint64 *stream_length, void *client_data) +{ + struct stat filestats; + CFlacDec * flacdec = (CFlacDec *)client_data; + + if(flacdec->mIn == stdin) + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + else if(fstat(fileno(flacdec->mIn), &filestats) != 0) + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + else { + *stream_length = (FLAC__uint64)filestats.st_size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +FLAC__bool flac_eof(const FLAC__StreamDecoder *, void *client_data) +{ + return feof(((CFlacDec *)client_data)->mIn)? true : false; +} + +FLAC__StreamDecoderWriteStatus flac_write(const FLAC__StreamDecoder * /*vf*/, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) +{ + CFlacDec * flacdec = (CFlacDec *)client_data; + + FLAC__uint32 test = 1; + bool is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true; + + if (flacdec->mFrameCount == 0) { + int fmt; + + flacdec->mChannels = frame->header.channels; + flacdec->mSampleRate = frame->header.sample_rate; + flacdec->mBps = frame->header.bits_per_sample; + flacdec->mBuffersize = MAX_OUTPUT_SAMPLES * flacdec->mChannels * flacdec->mBps / 8; + switch(flacdec->mBps) + { + case 8 : fmt = AFMT_U8; + break; + // TODO: + case 16 : + fmt = is_big_endian_host_ ? AFMT_S16_BE : AFMT_S16_LE; + break; + default: + printf("Flacdecoder: wrong bits per sample (%d)\n", flacdec->mBps); + flacdec->Status=CFlacDec::DSPSET_ERR; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + printf("SetDSP: mBps=%d, fmt = %d, Sample rate = %d, channels = %d\n", flacdec->mBps, fmt, flacdec->mSampleRate, flacdec->mChannels); + audioDecoder->PrepareClipPlay(flacdec->mChannels, flacdec->mSampleRate, flacdec->mBps, fmt == AFMT_S16_LE ? 1 : 0); +#if 0 +#ifdef DBOX + if (flacdec->SetDSP(flacdec->mOutputFd, fmt, flacdec->mSampleRate, flacdec->mChannels)) //FLAC__stream_decoder_get_channels (vf))) +#else + if (flacdec->SetDSP(flacdec->mOutputFd, AFMT_S16_NE, flacdec->mSampleRate, flacdec->mChannels)) +#endif + { + flacdec->Status=CFlacDec::DSPSET_ERR; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } +#endif + } + + const unsigned bps = frame->header.bits_per_sample, channels = frame->header.channels; + const unsigned shift = ((bps%8)? 8-(bps%8): 0); + FLAC__bool is_big_endian = false; + FLAC__bool is_unsigned_samples = bps<=8; + unsigned wide_samples = frame->header.blocksize, wide_sample, sample, channel, byte; +// unsigned frame_bytes = 0; + + static FLAC__int8 s8buffer[FLAC__MAX_BLOCK_SIZE * FLAC__MAX_CHANNELS * sizeof(FLAC__int32)]; /* WATCHOUT: can be up to 2 megs */ + FLAC__uint8 *u8buffer = (FLAC__uint8 *)s8buffer; + FLAC__int16 *s16buffer = (FLAC__int16 *)s8buffer; + FLAC__uint16 *u16buffer = (FLAC__uint16 *)s8buffer; + FLAC__int32 *s32buffer = (FLAC__int32 *)s8buffer; + FLAC__uint32 *u32buffer = (FLAC__uint32 *)s8buffer; + size_t bytes_to_write = 0; + + /* sanity-check the bits-per-sample */ + if(flacdec->mBps) { + if(bps != flacdec->mBps) { + if(flacdec->mFrameCount) + printf("ERROR, bits-per-sample is %u in frame but %u in STREAMINFO\n", bps, flacdec->mBps); + else + printf("ERROR, bits-per-sample is %u in this frame but %u in previous frames\n", bps, flacdec->mBps); + } + } + + /* sanity-check the #channels */ + if(flacdec->mChannels) { + if(channels != flacdec->mChannels) { + if(flacdec->mFrameCount) + printf("ERROR, channels is %u in frame but %u in STREAMINFO\n", channels, flacdec->mChannels); + else + printf("ERROR, channels is %u in this frame but %u in previous frames\n", channels, flacdec->mChannels); + } + } + + /* sanity-check the sample rate */ + if(flacdec->mSampleRate) { + if(frame->header.sample_rate != flacdec->mSampleRate) { + if(flacdec->mFrameCount) + printf("ERROR, sample rate is %u in frame but %u in STREAMINFO\n", frame->header.sample_rate, flacdec->mSampleRate); + else + printf("ERROR, sample rate is %u in this frame but %u in previous frames\n", frame->header.sample_rate, flacdec->mSampleRate); + } + } + + if(wide_samples > 0) { + flacdec->mSamplesProcessed += wide_samples; + flacdec->mFrameCount++; + // ripped from libFLAC examples/plugins + if(shift)/* && !decoder_session->replaygain.apply) */{ + for(wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++) + ((FLAC__int32**)buffer)[channel][wide_sample] <<= shift;/*@@@@@@un-const'ing the buffer is hacky but safe*/ + } + + /* first some special code for common cases */ + if(is_big_endian == is_big_endian_host_ && !is_unsigned_samples && channels == 2 && bps+shift == 16) { + FLAC__int16 *buf1_ = s16buffer + 1; + if(is_big_endian) + memcpy(s16buffer, ((FLAC__byte*)(buffer[0]))+2, sizeof(FLAC__int32) * wide_samples - 2); + else + memcpy(s16buffer, buffer[0], sizeof(FLAC__int32) * wide_samples); + for(sample = 0; sample < wide_samples; sample++, buf1_+=2) + *buf1_ = (FLAC__int16)buffer[1][sample]; + bytes_to_write = 4 * sample; + } + else if(is_big_endian == is_big_endian_host_ && !is_unsigned_samples && channels == 1 && bps+shift == 16) { + FLAC__int16 *buf1_ = s16buffer; + for(sample = 0; sample < wide_samples; sample++) + *buf1_++ = (FLAC__int16)buffer[0][sample]; + bytes_to_write = 2 * sample; + } + /* generic code for the rest */ + else if(bps+shift == 16) { + if(is_unsigned_samples) { + if(channels == 2) { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) { + u16buffer[sample++] = (FLAC__uint16)(buffer[0][wide_sample] + 0x8000); + u16buffer[sample++] = (FLAC__uint16)(buffer[1][wide_sample] + 0x8000); + } + } + else if(channels == 1) { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + u16buffer[sample++] = (FLAC__uint16)(buffer[0][wide_sample] + 0x8000); + } + else { /* works for any 'channels' but above flavors are faster for 1 and 2 */ + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++, sample++) + u16buffer[sample] = (FLAC__uint16)(buffer[channel][wide_sample] + 0x8000); + } + } + else { + if(channels == 2) { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) { + s16buffer[sample++] = (FLAC__int16)(buffer[0][wide_sample]); + s16buffer[sample++] = (FLAC__int16)(buffer[1][wide_sample]); + } + } + else if(channels == 1) { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + s16buffer[sample++] = (FLAC__int16)(buffer[0][wide_sample]); + } + else { /* works for any 'channels' but above flavors are faster for 1 and 2 */ + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++, sample++) + s16buffer[sample] = (FLAC__int16)(buffer[channel][wide_sample]); + } + } + if(is_big_endian != is_big_endian_host_) { + unsigned char tmp; + const unsigned bytes = sample * 2; + for(byte = 0; byte < bytes; byte += 2) { + tmp = u8buffer[byte]; + u8buffer[byte] = u8buffer[byte+1]; + u8buffer[byte+1] = tmp; + } + } + bytes_to_write = 2 * sample; + } + else if(bps+shift == 24) { + if(is_unsigned_samples) { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++, sample++) + u32buffer[sample] = buffer[channel][wide_sample] + 0x800000; + } + else { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++, sample++) + s32buffer[sample] = buffer[channel][wide_sample]; + } + if(is_big_endian != is_big_endian_host_) { + unsigned char tmp; + const unsigned bytes = sample * 4; + for(byte = 0; byte < bytes; byte += 4) { + tmp = u8buffer[byte]; + u8buffer[byte] = u8buffer[byte+3]; + u8buffer[byte+3] = tmp; + tmp = u8buffer[byte+1]; + u8buffer[byte+1] = u8buffer[byte+2]; + u8buffer[byte+2] = tmp; + } + } + if(is_big_endian) { + unsigned lbyte; + const unsigned bytes = sample * 4; + for(lbyte = byte = 0; byte < bytes; ) { + byte++; + u8buffer[lbyte++] = u8buffer[byte++]; + u8buffer[lbyte++] = u8buffer[byte++]; + u8buffer[lbyte++] = u8buffer[byte++]; + } + } + else { + unsigned lbyte; + const unsigned bytes = sample * 4; + for(lbyte = byte = 0; byte < bytes; ) { + u8buffer[lbyte++] = u8buffer[byte++]; + u8buffer[lbyte++] = u8buffer[byte++]; + u8buffer[lbyte++] = u8buffer[byte++]; + byte++; + } + } + bytes_to_write = 3 * sample; + } + else if(bps+shift == 8) { + if(is_unsigned_samples) { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++, sample++) + u8buffer[sample] = (FLAC__uint8)(buffer[channel][wide_sample] + 0x80); + } + else { + for(sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) + for(channel = 0; channel < channels; channel++, sample++) + s8buffer[sample] = (FLAC__int8)(buffer[channel][wide_sample]); + } + bytes_to_write = sample; + } + else { +// FLAC__ASSERT(0); + /* double protection */ + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + if(bytes_to_write > 0) { + ssize_t cnt; + unsigned int j=0; + while (jmBuffersize > bytes_to_write) + cnt = bytes_to_write - j; + else + cnt = flacdec->mBuffersize; + + //if (write(flacdec->mOutputFd, &u8buffer[j], cnt) != (ssize_t)cnt) + if (audioDecoder->WriteClip(&u8buffer[j], cnt) != cnt) + { + /* if a pipe closed when writing to stdout, we let it go without an error message */ + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + j += cnt; + } + } + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void flac_metadata(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *client_data) +{ + CFlacDec * flacdec = (CFlacDec *)client_data; + + switch (metadata->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + { + printf("STREAMINFO block received\n"); + flacdec->mTotalSamples = metadata->data.stream_info.total_samples; + flacdec->mBps = metadata->data.stream_info.bits_per_sample; + flacdec->mChannels = metadata->data.stream_info.channels; + flacdec->mSampleRate = metadata->data.stream_info.sample_rate; + + { + /* with VC++ you have to spoon feed it the casting from uint64->int64->double */ + + FLAC__uint64 l = (FLAC__uint64)((flacdec->mTotalSamples * 1000ULL) / flacdec->mSampleRate); + if (l > INT_MAX) + l = INT_MAX; + flacdec->mLengthInMsec = (int)l; + } + break; + } + case FLAC__METADATA_TYPE_PADDING: printf("PADDING block received\n"); break; + case FLAC__METADATA_TYPE_APPLICATION: printf("APPLICATION block received\n"); break; + case FLAC__METADATA_TYPE_SEEKTABLE: printf("SEEKTABLE block received\n"); break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + { + printf("VORBISCOMMENT block (a.k.a. FLAC tags) received\n"); + // if there is an old metadata -> clear it + if (flacdec->mMetadata != NULL) + FLAC__metadata_object_delete(flacdec->mMetadata); + flacdec->mMetadata = FLAC__metadata_object_clone(metadata); + break; + } + case FLAC__METADATA_TYPE_CUESHEET: printf("CUESHEET block received\n"); break; + case FLAC__METADATA_TYPE_PICTURE: printf("PICTURE block received\n"); break; + case FLAC__METADATA_TYPE_UNDEFINED: + default: printf("Undefined block received\n"); break; + } +} + +void flac_error(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus, void *) +{ +} + +CBaseDec::RetCode CFlacDec::Decoder(FILE *in, const int /*OutputFd*/, State* const state, CAudioMetaData* meta_data, time_t* const time_played, unsigned int* const secondsToSkip) +{ + int rval; +// FLAC__uint64 jumptime=0; + Status = OK; + mState = state; + mTimePlayed = time_played; + mFrameCount = 0; + mSamplesProcessed = 0; + + mFlacDec = FLAC__stream_decoder_new(); + + if (!Open(in, mFlacDec)) + { + Status=DATA_ERR; + return Status; + } + + /* up and away ... */ + mSlotSize = MAX_OUTPUT_SAMPLES * 2 * FLAC__stream_decoder_get_channels(mFlacDec); +// State oldstate=*state; + int jumppos=0; + int actSecsToSkip = (*secondsToSkip != 0) ? *secondsToSkip : MSECS_TO_SKIP / 1000; + int bytes_to_skip = (int) (1.0 * actSecsToSkip * meta_data->bitrate / 8); + int bytes_to_play = (int) (1.0 * MSECS_TO_PLAY / 1000 * meta_data->bitrate / 8); + unsigned int oldSecsToSkip = *secondsToSkip; +// FLAC__uint64 position; + + 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, 0, 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; + } + } + + if (FLAC__stream_decoder_get_state(mFlacDec) == FLAC__STREAM_DECODER_END_OF_STREAM) { + rval = false; + break; + } + + rval = FLAC__stream_decoder_process_single(mFlacDec); + // TODO: calculate time_played from the actual file position so that REW/FF actions will be included + *time_played = (mSamplesProcessed * (mLengthInMsec/1000)) / mTotalSamples; + + } while (rval && *state != STOP_REQ && Status == OK); + + // let buffer run dry + printf("let buffer run dry\n"); + while (rval == true && *state != STOP_REQ && Status == OK /* && mReadSlot != mWriteSlot*/) + { + printf("...drying - *state=%x, Status=%x\n", *state, Status); + usleep(100000); + } + + /* clean up the junk from the party */ + if (mMetadata) + FLAC__metadata_object_delete(mMetadata); + mMetadata = NULL; + FLAC__stream_decoder_finish(mFlacDec); + FLAC__stream_decoder_delete(mFlacDec); + mFlacDec = NULL; + + audioDecoder->StopClip(); + + /* and drive home ;) */ + return Status; +} + + +bool CFlacDec::GetMetaData(FILE *in, const bool /*nice*/, CAudioMetaData* m) +{ + FLAC__StreamDecoder *vf; + + vf = FLAC__stream_decoder_new(); + FLAC__stream_decoder_set_metadata_ignore_all(vf); + // we need streaminfo (default on) and FLAC__METADATA_TYPE_VORBIS_COMMENT (default off) for ID3 tags + FLAC__stream_decoder_set_metadata_respond(vf, FLAC__METADATA_TYPE_STREAMINFO); + FLAC__stream_decoder_set_metadata_respond(vf, FLAC__METADATA_TYPE_VORBIS_COMMENT); + if (!Open(in, vf)) + { + return false; + } + + if (FLAC__stream_decoder_process_until_end_of_metadata(vf)) { + // if the metadata callback was called mMetadata should be filled with infos + if (mMetadata) { + SetMetaData(mMetadata, m); + } + } + // clean up stuff, the mMetadata may be used later again so let the decoder free it + FLAC__stream_decoder_finish(vf); + FLAC__stream_decoder_delete(vf); + + return true; +} + +CFlacDec* CFlacDec::getInstance() +{ + static CFlacDec* FlacDec = NULL; + if(FlacDec == NULL) + { + FlacDec = new CFlacDec(); + } + return FlacDec; +} + +void CFlacDec::ParseUserComments(FLAC__StreamMetadata_VorbisComment* vc, CAudioMetaData* m) +{ + if(vc->vendor_string.entry) + printf("vendor_string = %d: %s\n", vc->vendor_string.length, vc->vendor_string.entry); + + printf("num_comments = %u\n", vc->num_comments); + for(unsigned int i = 0; i < vc->num_comments; i++) { + if(vc->comments[i].entry) { + printf("comments[%u] = %d: %s\n", i, vc->comments[i].length, vc->comments[i].entry); + const char *search; + if((search=strstr((const char*)vc->comments[i].entry,"Artist"))!=NULL || + (search=strstr((const char*)vc->comments[i].entry,"ARTIST"))!=NULL) + m->artist = search+7; + else if((search=strstr((const char*)vc->comments[i].entry,"Album"))!=NULL || + (search=strstr((const char*)vc->comments[i].entry,"ALBUM"))!=NULL) + m->album = search+6; + else if((search=strstr((const char*)vc->comments[i].entry,"Title"))!=NULL || + (search=strstr((const char*)vc->comments[i].entry,"TITLE"))!=NULL) + m->title = search+6; + else if((search=strstr((const char*)vc->comments[i].entry,"Genre"))!=NULL || + (search=strstr((const char*)vc->comments[i].entry,"GENRE"))!=NULL) + m->genre = search+6; + else if((search=strstr((const char*)vc->comments[i].entry,"Date"))!=NULL || + (search=strstr((const char*)vc->comments[i].entry,"DATE"))!=NULL) + m->date = search+5; + else if((search=strstr((const char*)vc->comments[i].entry,"TrackNumber"))!=NULL || + (search=strstr((const char*)vc->comments[i].entry,"TRACKNUMBER"))!=NULL) + m->track = search+12; + } + } +} + +void CFlacDec::SetMetaData(FLAC__StreamMetadata *metadata, CAudioMetaData* m) +{ + /* Set Metadata */ + m->type = CAudioMetaData::FLAC; + m->bitrate = mBps * mSampleRate; + m->samplerate = mSampleRate; + + m->total_time = (time_t) mLengthInMsec / 1000; + + std::stringstream ss; + ss << "FLAC V." << FLAC__VERSION_STRING << " / " << mChannels << "channel(s)"; + + m->type_info = ss.str(); + + ParseUserComments((FLAC__StreamMetadata_VorbisComment*)&(metadata->data.vorbis_comment), m); + m->changed=true; +} + +bool CFlacDec::Open(FILE* in, FLAC__StreamDecoder* vf) +{ + FLAC__StreamDecoderInitStatus rval; + + mIn = in; + + /* 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 ! */ + + /* test the dope ... */ + rval = FLAC__stream_decoder_init_stream(vf, + flac_read, + flac_seek, + flac_tell, + flac_length, + flac_eof, + flac_write, + flac_metadata, + flac_error, + (void *)this); + /* 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 FLAC__STREAM_DECODER_INIT_STATUS_OK: sprintf(err_txt, "Initialization was successful"); break; + case FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER: sprintf(err_txt, "The library was not compiled with support for the given container format"); break; + case FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS: sprintf(err_txt, "A required callback was not supplied"); break; + case FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR: sprintf(err_txt, "An error occurred allocating memory"); break; + case FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE: sprintf(err_txt, "fopen() failed in FLAC__stream_decoder_init_file() or FLAC__stream_decoder_init_ogg_file()"); break; + case FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED: sprintf(err_txt, "FLAC__stream_decoder_init_*() was called when the decoder was already initialized, usually because FLAC__stream_decoder_finish() was not called"); break; + default: sprintf(err_txt, "unknown error, code: %d", rval); + } +// fprintf(stderr,"%s: %s\n", ProgName, err_txt); + printf("%s: %s\n", ProgName, err_txt); + return false; + } + + /* finish the opening and ignite the joint */ +// mSeekable = true; + mSeekable = false; + + return true; +} diff --git a/src/driver/audiodec/flacdec.h b/src/driver/audiodec/flacdec.h new file mode 100644 index 000000000..8d92617b4 --- /dev/null +++ b/src/driver/audiodec/flacdec.h @@ -0,0 +1,77 @@ +/* + Neutrino-GUI - DBoxII-Project + + FLAC audio decoder + License: GPL + + ported from tuxbox-CVS flacdec.h CVS rev 1.1 + Neutrino-HD port is: + (C) 2011 Stefan Seyfried, B1 Systems GmbH, Vohburg, Germany + + 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, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA +*/ + + +#ifndef __FLAC_DEC__ +#define __FLAC_DEC__ + +#include +#include +#include +#include +#include +#include +#include + +#define DECODE_SLOTS 30 + +class CFlacDec : public CBaseDec +{ + +public: + static CFlacDec* 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); + CFlacDec(){ mMetadata = NULL; }; + + int mWriteSlot; + int mReadSlot; + int mSlotSize; + unsigned long mFrameCount; + unsigned int mChannels; + unsigned int mSampleRate; + unsigned long mBytes; + unsigned int mBps; + unsigned int mSamplesProcessed; + unsigned int mBuffersize; + unsigned long mTotalSamples; + unsigned int mLengthInMsec; + FLAC__StreamDecoder *mFlacDec; + FILE* mIn; + char* mPcmSlots[DECODE_SLOTS]; + FLAC__uint64 mSlotTime[DECODE_SLOTS]; + RetCode Status; + FLAC__StreamMetadata *mMetadata; + +private: + void ParseUserComments(FLAC__StreamMetadata_VorbisComment*, CAudioMetaData*); + bool Open(FILE*, FLAC__StreamDecoder*); + void SetMetaData(FLAC__StreamMetadata*, CAudioMetaData*); + State* mState; + bool mSeekable; + time_t* mTimePlayed; +}; + +#endif