You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
k3b/plugins/encoder/sox/k3bsoxencoder.cpp

483 lines
11 KiB

/*
*
* $Id: k3bsoxencoder.cpp 731310 2007-10-31 10:30:05Z trueg $
* Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
*
* This file is part of the K3b project.
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
*
* 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 <config.h>
#include "k3bsoxencoder.h"
#include "base_k3bsoxencoderconfigwidget.h"
#include <k3bprocess.h>
#include <k3bcore.h>
#include <k3bexternalbinmanager.h>
#include <k3bpluginfactory.h>
#include <kdebug.h>
#include <kconfig.h>
#include <klocale.h>
#include <tqfileinfo.h>
#include <tqfile.h>
#include <tqvalidator.h>
#include <tqlineedit.h>
#include <tqcombobox.h>
#include <tqcheckbox.h>
#include <tqlayout.h>
#include <sys/types.h>
#include <sys/wait.h>
K_EXPORT_COMPONENT_FACTORY( libk3bsoxencoder, K3bPluginFactory<K3bSoxEncoder>( "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* parent, const char* name )
: K3bAudioEncoder( parent, 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<TQCString>& args = d->process->args();
TQString s;
for( TQValueList<TQCString>::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* parent,
const char* name ) const
{
return new K3bSoxEncoderSettingsWidget( parent, name );
}
K3bSoxEncoderSettingsWidget::K3bSoxEncoderSettingsWidget( TQWidget* parent, const char* name )
: K3bPluginConfigWidget( parent, 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"