mirror of
https://github.com/tuxbox-fork-migrations/recycled-ni-neutrino.git
synced 2025-09-01 01:41:12 +02:00
add FLAC format to audio decoders
git-svn-id: file:///home/bas/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@1539 e54a6e83-5905-42d5-8d5c-058d10e6a962
Origin commit data
------------------
Commit: 4dc844298e
Author: Stefan Seyfried <seife@tuxbox-git.slipkontur.de>
Date: 2011-06-13 (Mon, 13 Jun 2011)
This commit is contained in:
657
src/driver/audiodec/flacdec.cpp
Normal file
657
src/driver/audiodec/flacdec.cpp
Normal file
@@ -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 <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <flacdec.h>
|
||||||
|
#include <linux/soundcard.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
#include <driver/netfile.h>
|
||||||
|
|
||||||
|
#include <zapit/include/audio.h>
|
||||||
|
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 (j<bytes_to_write) {
|
||||||
|
if (j+flacdec->mBuffersize > 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;
|
||||||
|
}
|
77
src/driver/audiodec/flacdec.h
Normal file
77
src/driver/audiodec/flacdec.h
Normal file
@@ -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 <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <basedec.h>
|
||||||
|
#include <driver/audiometadata.h>
|
||||||
|
#include <FLAC/all.h>
|
||||||
|
|
||||||
|
#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
|
Reference in New Issue
Block a user