diff options
Diffstat (limited to 'vdr_decoder_sndfile.c')
-rw-r--r-- | vdr_decoder_sndfile.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/vdr_decoder_sndfile.c b/vdr_decoder_sndfile.c new file mode 100644 index 0000000..0b84eaf --- /dev/null +++ b/vdr_decoder_sndfile.c @@ -0,0 +1,412 @@ +/* + * MP3/MPlayer plugin to VDR (C++) + * + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de> + * + * This code 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 code 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. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifdef HAVE_SNDFILE + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <math.h> + +#include <tools.h> + +#include "mg_setup.h" +#include "vdr_decoder_sndfile.h" +#include "i18n.h" + +#ifndef SNDFILE_1 +#error You must use libsndfile version 1.x.x +#endif + +#define CDFS_TRACK_OFF 150 +#define CDFS_PROC "/proc/cdfs" +#define CDFS_MARK_ID "CD (discid=%x) contains %d tracks:" +#define CDFS_MARK_TR "%*[^[][ %d - %d" +#define CDFS_TRACK "track-" + +#define CDDB_PROTO 5 // used protocol level +#define CDDB_TOUT 30*1000 // connection timeout (ms) + +#ifdef DEBUG_CDFS +#define dc(x) { (x); } +#else +#define dc(x) ; +#endif + +// --- mgSndfileDecoder ------------------------------------------------------------- + +#define SF_SAMPLES (sizeof(m_pcm->samples[0])/sizeof(mad_fixed_t)) + +mgSndfileDecoder::mgSndfileDecoder( mgItemGd *item ) + : mgDecoder( item ), m_file( item ) +{ + + m_pcm = 0; + m_framebuff = 0; + m_playing = false; + m_ready = false; +} + +mgSndfileDecoder::~mgSndfileDecoder() +{ + clean(); +} + +bool mgSndfileDecoder::valid(void) +{ + bool res = false; + + if( tryLock() ) + { + if( m_file.Open(false) ) + { + res=true; + } + mgDecoder::unlock(); + } + + return res; +} + +mgPlayInfo * +mgSndfileDecoder::playInfo () +{ + mgPlayInfo *pi = NULL; + + return pi; +} + +void mgSndfileDecoder::init(void) +{ + clean(); + + m_pcm = new struct mad_pcm; + m_framebuff = MALLOC( int, 2*SF_SAMPLES + 8 ); + +#ifdef GUARD_DEBUG + for(int i=0; i<8; i++) m_framebuff[i+(SF_SAMPLES*2)-4]=0xdeadbeaf; +#endif + + m_index = 0; +} + +bool mgSndfileDecoder::clean(void) +{ + m_playing = false; + + m_buffMutex.Lock(); + m_run = false; + m_bgCond.Broadcast(); + m_buffMutex.Unlock(); + + cThread::Cancel(3); + + m_buffMutex.Lock(); + if( !m_ready ) + { + m_deferedN = -1; + m_ready=true; + } + m_fgCond.Broadcast(); + m_buffMutex.Unlock(); + + delete m_pcm; + m_pcm=0; + +#ifdef GUARD_DEBUG + if(m_framebuff) + { + printf("snd: bufferguard"); + for(int i=0; i<8; i++) printf(" %08x",m_framebuff[i+(SF_SAMPLES*2)-4]); + printf("\n"); + } +#endif + + free(m_framebuff); + m_framebuff=0; + m_file.Close(); + + return false; +} + +bool mgSndfileDecoder::start(void) +{ + mgDecoder::lock(true); + init(); + m_playing=true; + + if( m_file.Open() ) + { + // d(printf("snd: open rate=%d frames=%lld channels=%d format=0x%x seek=%d\n", + // m_file.SoundfileInfo()->samplerate,m_file.SoundfileInfo()->frames,m_file.SoundfileInfo()->channels,m_file.SoundfileInfo()->format,m_file.SoundfileInfo()->seekable)) + + if( m_file.SoundfileInfo()->channels <= 2 ) + { + m_ready = false; + m_run = true; + m_softCount=0; + + cThread::Start(); + mgDecoder::unlock(); + return true; + } + else + { + // esyslog("ERROR: cannot play sound file %s: more than 2 channels", m_filename.c_str() ); + } + } + mgDecoder::unlock(); + return clean(); +} + +bool mgSndfileDecoder::stop(void) +{ + mgDecoder::lock(); + if( m_playing ) + { + clean(); + } + mgDecoder::unlock(); + return true; +} + +void mgSndfileDecoder::Action(void) +{ + m_buffMutex.Lock(); + while( m_run ) + { + + if( m_ready ) + { + m_bgCond.Wait(m_buffMutex); + } + + if(!m_ready) + { + m_buffMutex.Unlock(); + m_deferedN = m_file.Stream( m_framebuff,SF_SAMPLES ); + m_buffMutex.Lock(); + m_ready = true; + m_fgCond.Broadcast(); + } + } + m_buffMutex.Unlock(); +} + +struct mgDecode *mgSndfileDecoder::done(eDecodeStatus status) +{ + m_ds.status = status; + m_ds.index = m_index*1000 / m_file.SoundfileInfo()->samplerate; + m_ds.pcm = m_pcm; + + mgDecoder::unlock(); // release the lock from Decode() + + return &m_ds; +} + +struct mgDecode *mgSndfileDecoder::decode() +{ + mgDecoder::lock(); // this is released in Done() + if(m_playing) + { + cMutexLock lock(&m_buffMutex); + while(!m_ready) + { + if( !m_softCount || !m_fgCond.TimedWait( m_buffMutex, m_softCount*5 ) ) + { + if( m_softCount < 20 ) + { + m_softCount++; + } + return done(dsSoftError); + } + } + m_softCount = 0; + m_ready = false; + m_bgCond.Broadcast(); + + int n = m_deferedN; + if( n < 0 ) + { + return done(dsError); + } + + if( n == 0 ) + { + return done(dsEof); + } + + m_pcm->samplerate = m_file.SoundfileInfo()->samplerate; + m_pcm->channels = m_file.SoundfileInfo()->channels; + m_pcm->length = n; + m_index += n; + + int *data = m_framebuff; + mad_fixed_t *sam0 = m_pcm->samples[0], *sam1=m_pcm->samples[1]; + + const int s = (sizeof(int)*8)-1-MAD_F_FRACBITS; // shift value for mad_fixed conversion + + if(m_pcm->channels>1) + { + for(; n>0 ; n--) + { + *sam0++=(*data++) >> s; + *sam1++=(*data++) >> s; + } + } + else + { + for(; n>0 ; n--) + { + *sam0++=(*data++) >> s; + } + } + return done(dsPlay); + } + return done(dsError); +} + +bool mgSndfileDecoder::skip( int seconds, int avail, int rate ) +{ + mgDecoder::lock(); + bool res=false; + float bsecs = (float) avail / (float) ( rate * (16 / 8 * 2) ); + + if( m_playing && m_file.SoundfileInfo()->seekable ) + { + float fsecs = (float)seconds - bsecs; + sf_count_t frames = (sf_count_t)( fsecs*(float)m_file.SoundfileInfo()->samplerate ); + sf_count_t newpos = m_file.Seek( 0, true ) + frames; + + if( newpos > m_file.SoundfileInfo()->frames ) + { + newpos = m_file.SoundfileInfo()->frames-1; + } + + if( newpos < 0 ) + { + newpos=0; + } + + // d(printf("snd: skip: secs=%d fsecs=%f frames=%lld current=%lld new=%lld\n",Seconds,fsecs,frames,m_file.Seek(0,true),newpos)) + + m_buffMutex.Lock(); + frames = m_file.Seek( newpos, false ); + m_ready = false; + m_bgCond.Broadcast(); + m_buffMutex.Unlock(); + + if( frames >= 0 ) + { + m_index = frames; +#ifdef DEBUG + int i = frames/m_file.SoundfileInfo()->samplerate; + printf("snd: skipping to %02d:%02d (frame %lld)\n",i/60,i%60,frames); +#endif + res=true; + } + } + mgDecoder::unlock(); + return res; +} + +// --- cSndfile ---------------------------------------------------------------- + +mgSndfile::mgSndfile( mgItemGd *item ) +{ + m_filename = item->getSourceFile(); + m_sf = 0; +} + +mgSndfile::~mgSndfile() +{ + Close(); +} + +SF_INFO* mgSndfile::SoundfileInfo() +{ + return &m_sfi; +} + +bool mgSndfile::Open(bool log) +{ + if( m_sf ) + { + return( Seek() >= 0 ); + } + + m_sf = sf_open( m_filename.c_str(), SFM_READ, &m_sfi ); + if( !m_sf && log ) + { + Error( "open" ); + } + + return ( m_sf != 0 ); +} + +void mgSndfile::Close(void) +{ + if( m_sf ) + { + sf_close( m_sf ); + m_sf = 0; + } +} + +void mgSndfile::Error(const char *action) +{ + char buff[128]; + sf_error_str( m_sf, buff, sizeof(buff) ); + // esyslog( "ERROR: sndfile %s failed on %s: %s", action, Filename, buff ); +} + +sf_count_t mgSndfile::Seek( sf_count_t frames, bool relative ) +{ + int dir = SEEK_CUR; + if( !relative ) + { + dir = SEEK_SET; + } + + int n = sf_seek( m_sf, frames, dir ); + if( n < 0 ) + { + Error("seek"); + } + + return n; +} + +sf_count_t mgSndfile::Stream( int *buffer, sf_count_t frames ) +{ + sf_count_t n = sf_readf_int( m_sf, buffer, frames ); + if( n < 0 ) + { + Error("read"); + } + return n; +} + +#endif //HAVE_SNDFILE |