|
|
|
/*
|
|
|
|
*
|
|
|
|
* $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 <tdeconfig.h>
|
|
|
|
#include <tdelocale.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
|
|
|
|
TDEProcess vp;
|
|
|
|
K3bProcessOutputCollector out( &vp );
|
|
|
|
|
|
|
|
vp << path << "-h";
|
|
|
|
if( vp.start( TDEProcess::Block, TDEProcess::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( TDEProcess* 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(TDEProcess*)),
|
|
|
|
this, TQT_SLOT(slotSoxFinished(TDEProcess*)) );
|
|
|
|
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;
|
|
|
|
|
|
|
|
TDEConfig* 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( TDEProcess::NotifyOnExit, TDEProcess::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
|
|
|
|
TDEConfig* 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()
|
|
|
|
{
|
|
|
|
TDEConfig* 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()
|
|
|
|
{
|
|
|
|
TDEConfig* 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"
|