/* * * $Id: k3bsoxencoder.cpp 731310 2007-10-31 10:30:05Z trueg $ * Copyright (C) 2003 Sebastian Trueg * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg * * 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. * See the file "COPYING" for the exact licensing terms. */ #include #include "k3bsoxencoder.h" #include "base_k3bsoxencoderconfigwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_EXPORT_COMPONENT_FACTORY( libk3bsoxencoder, K3bPluginFactory( "libk3bsoxencoder" ) ) // the sox external program class K3bSoxProgram : public K3bExternalProgram { public: K3bSoxProgram() : K3bExternalProgram( "sox" ) { } bool scan( const TQString& p ) { if( p.isEmpty() ) return false; TQString path = p; TQFileInfo fi( path ); if( fi.isDir() ) { if( path[path.length()-1] != '/' ) path.append("/"); path.append("sox"); } if( !TQFile::exists( path ) ) return false; K3bExternalBin* bin = 0; // probe version KProcess vp; K3bProcessOutputCollector out( &vp ); vp << path << "-h"; if( vp.start( KProcess::Block, KProcess::AllOutput ) ) { int pos = out.output().find( "sox: SoX Version" ); if ( pos < 0 ) pos = out.output().find( "sox: SoX v" ); // newer sox versions int endPos = out.output().find( "\n", pos ); if( pos > 0 && endPos > 0 ) { pos += 17; bin = new K3bExternalBin( this ); bin->path = path; bin->version = out.output().mid( pos, endPos-pos ); addBin( bin ); return true; } else { pos = out.output().find( "sox: Version" ); endPos = out.output().find( "\n", pos ); if( pos > 0 && endPos > 0 ) { pos += 13; bin = new K3bExternalBin( this ); bin->path = path; bin->version = out.output().mid( pos, endPos-pos ); addBin( bin ); return true; } else return false; } } else return false; } }; class K3bSoxEncoder::Private { public: Private() : process(0) { } K3bProcess* process; TQString fileName; }; K3bSoxEncoder::K3bSoxEncoder( TQObject* tqparent, const char* name ) : K3bAudioEncoder( tqparent, name ) { if( k3bcore->externalBinManager()->program( "sox" ) == 0 ) k3bcore->externalBinManager()->addProgram( new K3bSoxProgram() ); d = new Private(); } K3bSoxEncoder::~K3bSoxEncoder() { delete d->process; delete d; } void K3bSoxEncoder::finishEncoderInternal() { if( d->process ) { if( d->process->isRunning() ) { ::close( d->process->stdinFd() ); // this is kind of evil... // but we need to be sure the process exited when this method returnes ::waitpid( d->process->pid(), 0, 0 ); } } } void K3bSoxEncoder::slotSoxFinished( KProcess* p ) { if( !p->normalExit() || p->exitStatus() != 0 ) kdDebug() << "(K3bSoxEncoder) sox exited with error." << endl; } bool K3bSoxEncoder::openFile( const TQString& ext, const TQString& filename, const K3b::Msf& ) { d->fileName = filename; return initEncoderInternal( ext ); } void K3bSoxEncoder::closeFile() { finishEncoderInternal(); } bool K3bSoxEncoder::initEncoderInternal( const TQString& extension ) { const K3bExternalBin* soxBin = k3bcore->externalBinManager()->binObject( "sox" ); if( soxBin ) { delete d->process; d->process = new K3bProcess(); d->process->setSplitStdout(true); d->process->setRawStdin(true); connect( d->process, TQT_SIGNAL(processExited(KProcess*)), this, TQT_SLOT(slotSoxFinished(KProcess*)) ); connect( d->process, TQT_SIGNAL(stderrLine(const TQString&)), this, TQT_SLOT(slotSoxOutputLine(const TQString&)) ); connect( d->process, TQT_SIGNAL(stdoutLine(const TQString&)), this, TQT_SLOT(slotSoxOutputLine(const TQString&)) ); // input settings *d->process << soxBin->path << "-t" << "raw" // raw samples << "-r" << "44100" // samplerate << "-s" // signed linear << "-w" // 16-bit words << "-c" << "2" // stereo << "-"; // read from stdin // output settings *d->process << "-t" << extension; KConfig* c = k3bcore->config(); c->setGroup( "K3bSoxEncoderPlugin" ); if( c->readBoolEntry( "manual settings", false ) ) { *d->process << "-r" << TQString::number( c->readNumEntry( "samplerate", 44100 ) ) << "-c" << TQString::number( c->readNumEntry( "channels", 2 ) ); int size = c->readNumEntry( "data size", 16 ); *d->process << ( size == 8 ? TQString("-b") : ( size == 32 ? TQString("-l") : TQString("-w") ) ); TQString encoding = c->readEntry( "data encoding", "signed" ); if( encoding == "unsigned" ) *d->process << "-u"; else if( encoding == "u-law" ) *d->process << "-U"; else if( encoding == "A-law" ) *d->process << "-A"; else if( encoding == "ADPCM" ) *d->process << "-a"; else if( encoding == "IMA_ADPCM" ) *d->process << "-i"; else if( encoding == "GSM" ) *d->process << "-g"; else if( encoding == "Floating-point" ) *d->process << "-f"; else *d->process << "-s"; } *d->process << d->fileName; kdDebug() << "***** sox parameters:" << endl; const TQValueList& args = d->process->args(); TQString s; for( TQValueList::const_iterator it = args.begin(); it != args.end(); ++it ) { s += *it + " "; } kdDebug() << s << flush << endl; return d->process->start( KProcess::NotifyOnExit, KProcess::All ); } else { kdDebug() << "(K3bSoxEncoder) could not find sox bin." << endl; return false; } } long K3bSoxEncoder::encodeInternal( const char* data, TQ_ULONG len ) { if( d->process ) { if( d->process->isRunning() ) return ::write( d->process->stdinFd(), (const void*)data, len ); else return -1; } else return -1; } void K3bSoxEncoder::slotSoxOutputLine( const TQString& line ) { kdDebug() << "(sox) " << line << endl; } TQStringList K3bSoxEncoder::extensions() const { static TQStringList s_extensions; if( s_extensions.isEmpty() ) { s_extensions << "au" << "8svx" << "aiff" << "avr" << "cdr" << "cvs" << "dat" << "gsm" << "hcom" << "maud" << "sf" << "sph" << "smp" << "txw" << "vms" << "voc" << "wav" << "wve" << "raw"; } if( k3bcore->externalBinManager()->foundBin( "sox" ) ) return s_extensions; else return TQStringList(); // no sox -> no encoding } TQString K3bSoxEncoder::fileTypeComment( const TQString& ext ) const { if( ext == "au" ) return i18n("Sun AU"); else if( ext == "8svx" ) return i18n("Amiga 8SVX"); else if( ext == "aiff" ) return i18n("AIFF"); else if( ext == "avr" ) return i18n("Audio Visual Research"); else if( ext == "cdr" ) return i18n("CD-R"); else if( ext == "cvs" ) return i18n("CVS"); else if( ext == "dat" ) return i18n("Text Data"); else if( ext == "gsm" ) return i18n("GSM Speech"); else if( ext == "hcom" ) return i18n("Macintosh HCOM"); else if( ext == "maud" ) return i18n("Maud (Amiga)"); else if( ext == "sf" ) return i18n("IRCAM"); else if( ext == "sph" ) return i18n("SPHERE"); else if( ext == "smp" ) return i18n("Turtle Beach SampleVision"); else if( ext == "txw" ) return i18n("Yamaha TX-16W"); else if( ext == "vms" ) return i18n("VMS"); else if( ext == "voc" ) return i18n("Sound Blaster VOC"); else if( ext == "wav" ) return i18n("Wave (Sox)"); else if( ext == "wve" ) return i18n("Psion 8-bit A-law"); else if( ext == "raw" ) return i18n("Raw"); else return i18n("Error"); } long long K3bSoxEncoder::fileSize( const TQString&, const K3b::Msf& msf ) const { // for now we make a rough assumption based on the settings KConfig* c = k3bcore->config(); c->setGroup( "K3bSoxEncoderPlugin" ); if( c->readBoolEntry( "manual settings", false ) ) { int sr = c->readNumEntry( "samplerate", 44100 ); int ch = c->readNumEntry( "channels", 2 ); int wsize = c->readNumEntry( "data size", 16 ); return msf.totalFrames()*sr*ch*wsize/75; } else { // fallback to raw return msf.audioBytes(); } } K3bPluginConfigWidget* K3bSoxEncoder::createConfigWidget( TQWidget* tqparent, const char* name ) const { return new K3bSoxEncoderSettingsWidget( tqparent, name ); } K3bSoxEncoderSettingsWidget::K3bSoxEncoderSettingsWidget( TQWidget* tqparent, const char* name ) : K3bPluginConfigWidget( tqparent, name ) { w = new base_K3bSoxEncoderConfigWidget( this ); w->m_editSamplerate->setValidator( new TQIntValidator( TQT_TQOBJECT(w->m_editSamplerate) ) ); TQHBoxLayout* lay = new TQHBoxLayout( this ); lay->setMargin( 0 ); lay->addWidget( w ); } K3bSoxEncoderSettingsWidget::~K3bSoxEncoderSettingsWidget() { } void K3bSoxEncoderSettingsWidget::loadConfig() { KConfig* c = k3bcore->config(); c->setGroup( "K3bSoxEncoderPlugin" ); w->m_checkManual->setChecked( c->readBoolEntry( "manual settings", false ) ); int channels = c->readNumEntry( "channels", 2 ); w->m_comboChannels->setCurrentItem( channels == 4 ? 2 : channels-1 ); w->m_editSamplerate->setText( TQString::number( c->readNumEntry( "samplerate", 44100 ) ) ); TQString encoding = c->readEntry( "data encoding", "signed" ); if( encoding == "unsigned" ) w->m_comboEncoding->setCurrentItem(1); else if( encoding == "u-law" ) w->m_comboEncoding->setCurrentItem(2); else if( encoding == "A-law" ) w->m_comboEncoding->setCurrentItem(3); else if( encoding == "ADPCM" ) w->m_comboEncoding->setCurrentItem(4); else if( encoding == "IMA_ADPCM" ) w->m_comboEncoding->setCurrentItem(5); else if( encoding == "GSM" ) w->m_comboEncoding->setCurrentItem(6); else if( encoding == "Floating-point" ) w->m_comboEncoding->setCurrentItem(7); else w->m_comboEncoding->setCurrentItem(0); int size = c->readNumEntry( "data size", 16 ); w->m_comboSize->setCurrentItem( size == 8 ? 0 : ( size == 32 ? 2 : 1 ) ); } void K3bSoxEncoderSettingsWidget::saveConfig() { KConfig* c = k3bcore->config(); c->setGroup( "K3bSoxEncoderPlugin" ); c->writeEntry( "manual settings", w->m_checkManual->isChecked() ); c->writeEntry( "channels", w->m_comboChannels->currentItem() == 0 ? 1 : ( w->m_comboChannels->currentItem() == 2 ? 4 : 2 ) ); c->writeEntry( "data size", w->m_comboSize->currentItem() == 0 ? 8 : ( w->m_comboSize->currentItem() == 2 ? 32 : 16 ) ); c->writeEntry( "samplerate", w->m_editSamplerate->text().toInt() ); TQString enc; switch( w->m_comboEncoding->currentItem() ) { case 1: enc = "unsigned"; break; case 2: enc = "u-law"; break; case 3: enc = "A-law"; break; case 4: enc = "ADPCM"; break; case 5: enc = "IMA_ADPCM"; break; case 6: enc = "GSM"; break; case 7: enc = "Floating-point"; break; default: enc = "signed"; break; } c->writeEntry( "data encoding", enc ); } #include "k3bsoxencoder.moc"