File indexing completed on 2024-05-26 04:54:21

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "k3bsoxencoder.h"
0010 #include "k3bsoxencoderdefaults.h"
0011 
0012 #include <config-k3b.h>
0013 
0014 #include "k3bprocess.h"
0015 #include "k3bcore.h"
0016 #include "k3bexternalbinmanager.h"
0017 #include "k3bplugin_i18n.h"
0018 
0019 #include <KConfig>
0020 #include <KConfigGroup>
0021 #include <KSharedConfig>
0022 
0023 #include <QDebug>
0024 #include <QFile>
0025 #include <QFileInfo>
0026 
0027 #include <sys/types.h>
0028 
0029 
0030 K_PLUGIN_CLASS_WITH_JSON(K3bSoxEncoder , "k3bsoxencoder.json")
0031 
0032 
0033 namespace {
0034 
0035 // the sox external program
0036 class SoxProgram : public K3b::ExternalProgram
0037 {
0038 public:
0039     SoxProgram()
0040         : K3b::ExternalProgram( "sox" ) {
0041     }
0042 
0043     bool scan( const QString& p ) override {
0044         if( p.isEmpty() )
0045             return false;
0046 
0047         QString path = p;
0048         QFileInfo fi( path );
0049         if( fi.isDir() ) {
0050             path = buildProgramPath( path, "sox" );
0051         }
0052 
0053         if( !QFile::exists( path ) )
0054             return false;
0055 
0056         K3b::ExternalBin* bin = 0;
0057 
0058         // probe version
0059         KProcess vp;
0060         vp.setOutputChannelMode( KProcess::MergedChannels );
0061 
0062         vp << path << "--version";
0063         vp.start();
0064         if( vp.waitForFinished( -1 ) ) {
0065             QByteArray out = vp.readAll();
0066             int pos = out.indexOf( "sox: SoX Version" );
0067             if ( pos >= 0 ) {
0068                 pos += 17;
0069             }
0070             else if ( ( pos = out.indexOf( "sox:      SoX v" ) ) >= 0 ) {
0071                 pos += 15;
0072             }
0073             else if ( ( pos = out.indexOf( "sox: SoX v" ) ) >= 0 ) {
0074                 pos += 10;
0075             }
0076             else if ( ( pos = out.indexOf( "sox: Version" ) ) >= 0 ) {
0077                 pos += 13;
0078             }
0079             int endPos = out.indexOf( '\n', pos );
0080             if( pos > 0 && endPos > 0 ) {
0081                 bin = new K3b::ExternalBin( *this, path );
0082                 bin->setVersion( K3b::Version( out.mid( pos, endPos-pos ) ) );
0083 
0084                 addBin( bin );
0085 
0086                 return true;
0087             }
0088         }
0089 
0090         return false;
0091     }
0092 };
0093 
0094 } // namespace
0095 
0096 class K3bSoxEncoder::Private
0097 {
0098 public:
0099     K3b::Process* process;
0100     QString fileName;
0101 };
0102 
0103 
0104 K3bSoxEncoder::K3bSoxEncoder( QObject* parent, const QVariantList& )
0105     : K3b::AudioEncoder( parent )
0106 {
0107     if( k3bcore->externalBinManager()->program( "sox" ) == 0 )
0108         k3bcore->externalBinManager()->addProgram( new SoxProgram() );
0109 
0110     d = new Private();
0111     d->process = 0;
0112 }
0113 
0114 
0115 K3bSoxEncoder::~K3bSoxEncoder()
0116 {
0117     delete d->process;
0118     delete d;
0119 }
0120 
0121 
0122 void K3bSoxEncoder::finishEncoderInternal()
0123 {
0124     if( d->process && d->process->isRunning() ) {
0125         d->process->closeWriteChannel();
0126 
0127         // this is kind of evil...
0128         // but we need to be sure the process exited when this method returns
0129         d->process->waitForFinished(-1);
0130     }
0131 }
0132 
0133 
0134 void K3bSoxEncoder::slotSoxFinished( int exitCode, QProcess::ExitStatus exitStatus )
0135 {
0136     if( (exitStatus != QProcess::NormalExit) || (exitCode != 0) )
0137         qDebug() << "(K3bSoxEncoder) sox exited with error.";
0138 }
0139 
0140 
0141 bool K3bSoxEncoder::openFile( const QString& extension, const QString& filename, const K3b::Msf& length, const MetaData& metaData )
0142 {
0143     d->fileName = filename;
0144     return initEncoderInternal( extension, length, metaData );
0145 }
0146 
0147 
0148 bool K3bSoxEncoder::isOpen() const
0149 {
0150     return d->process && d->process->isRunning();
0151 }
0152 
0153 
0154 void K3bSoxEncoder::closeFile()
0155 {
0156     finishEncoderInternal();
0157 }
0158 
0159 
0160 bool K3bSoxEncoder::initEncoderInternal( const QString& extension, const K3b::Msf& /*length*/, const MetaData& /*metaData*/ )
0161 {
0162     const K3b::ExternalBin* soxBin = k3bcore->externalBinManager()->binObject( "sox" );
0163     if( soxBin ) {
0164         // we want to be thread-safe
0165         delete d->process;
0166         d->process = new K3b::Process();
0167         d->process->setSplitStdout(true);
0168 
0169         connect( d->process, SIGNAL(finished(int,QProcess::ExitStatus)),
0170                  this, SLOT(slotSoxFinished(int,QProcess::ExitStatus)) );
0171         connect( d->process, SIGNAL(stdoutLine(QString)),
0172                  this, SLOT(slotSoxOutputLine(QString)) );
0173 
0174         // input settings
0175         *d->process << soxBin->path()
0176                     << "-t" << "raw"    // raw samples
0177                     << "-r" << "44100"  // samplerate
0178                     << "-s";            // signed linear
0179         if ( soxBin->version() >= K3b::Version( 13, 0, 0 ) )
0180             *d->process << "-2";
0181         else
0182             *d->process << "-w";        // 16-bit words
0183         *d->process << "-c" << "2"      // stereo
0184                     << "-";             // read from stdin
0185 
0186         // output settings
0187         *d->process << "-t" << extension;
0188 
0189         KSharedConfig::Ptr c = KSharedConfig::openConfig();
0190         KConfigGroup grp(c, QStringLiteral("K3bSoxEncoderPlugin") );
0191         if( grp.readEntry( "manual settings", DEFAULT_MANUAL_SETTINGS ) ) {
0192             *d->process << "-r" << QString::number( grp.readEntry( "samplerate", DEFAULT_SAMPLE_RATE ) )
0193                         << "-c" << QString::number( grp.readEntry( "channels", DEFAULT_CHANNELS ) );
0194 
0195             int size = grp.readEntry( "data size", DEFAULT_DATA_SIZE );
0196             *d->process << ( size == 8 ? QString("-b") : ( size == 32 ? QString("-l") : QString("-w") ) );
0197 
0198             QString encoding = grp.readEntry( "data encoding", DEFAULT_DATA_ENCODING );
0199             if( encoding == "unsigned" )
0200                 *d->process << "-u";
0201             else if( encoding == "u-law" )
0202                 *d->process << "-U";
0203             else if( encoding == "A-law" )
0204                 *d->process << "-A";
0205             else if( encoding == "ADPCM" )
0206                 *d->process << "-a";
0207             else if( encoding == "IMA_ADPCM" )
0208                 *d->process << "-i";
0209             else if( encoding == "GSM" )
0210                 *d->process << "-g";
0211             else if( encoding == "Floating-point" )
0212                 *d->process << "-f";
0213             else
0214                 *d->process << "-s";
0215         }
0216 
0217         *d->process << d->fileName;
0218 
0219         qDebug() << "***** sox parameters:";
0220         QString s = d->process->joinedArgs();
0221         qDebug() << s << Qt::flush;
0222 
0223         return d->process->start( KProcess::MergedChannels );
0224     }
0225     else {
0226         qDebug() << "(K3bSoxEncoder) could not find sox bin.";
0227         return false;
0228     }
0229 }
0230 
0231 
0232 qint64 K3bSoxEncoder::encodeInternal( const char* data, qint64 len )
0233 {
0234     if( d->process && d->process->isRunning() )
0235         return d->process->write( data, len );
0236     else
0237         return -1;
0238 }
0239 
0240 
0241 void K3bSoxEncoder::slotSoxOutputLine( const QString& line )
0242 {
0243     qDebug() << "(sox) " << line;
0244 }
0245 
0246 
0247 QStringList K3bSoxEncoder::extensions() const
0248 {
0249     static QStringList s_extensions;
0250     if( s_extensions.isEmpty() ) {
0251         s_extensions << "au"
0252                      << "8svx"
0253                      << "aiff"
0254                      << "avr"
0255                      << "cdr"
0256                      << "cvs"
0257                      << "dat"
0258                      << "gsm"
0259                      << "hcom"
0260                      << "maud"
0261                      << "sf"
0262                      << "sph"
0263                      << "smp"
0264                      << "txw"
0265                      << "vms"
0266                      << "voc"
0267                      << "wav"
0268                      << "wve"
0269                      << "raw";
0270     }
0271 
0272     if( k3bcore->externalBinManager()->foundBin( "sox" ) )
0273         return s_extensions;
0274     else
0275         return QStringList(); // no sox -> no encoding
0276 }
0277 
0278 
0279 QString K3bSoxEncoder::fileTypeComment( const QString& ext ) const
0280 {
0281     if( ext == "au" )
0282         return i18n("Sun AU");
0283     else if( ext == "8svx" )
0284         return i18n("Amiga 8SVX");
0285     else if( ext == "aiff" )
0286         return i18n("AIFF");
0287     else if( ext == "avr" )
0288         return i18n("Audio Visual Research");
0289     else if( ext == "cdr" )
0290         return i18n("CD-R");
0291     else if( ext == "cvs" )
0292         return i18n("CVS");
0293     else if( ext == "dat" )
0294         return i18n("Text Data");
0295     else if( ext == "gsm" )
0296         return i18n("GSM Speech");
0297     else if( ext == "hcom" )
0298         return i18n("Macintosh HCOM");
0299     else if( ext == "maud" )
0300         return i18n("Maud (Amiga)");
0301     else if( ext == "sf" )
0302         return i18n("IRCAM");
0303     else if( ext == "sph" )
0304         return i18n("SPHERE");
0305     else if( ext == "smp" )
0306         return i18n("Turtle Beach SampleVision");
0307     else if( ext == "txw" )
0308         return i18n("Yamaha TX-16W");
0309     else if( ext == "vms" )
0310         return i18n("VMS");
0311     else if( ext == "voc" )
0312         return i18n("Sound Blaster VOC");
0313     else if( ext == "wav" )
0314         return i18n("Wave (SoX)");
0315     else if( ext == "wve" )
0316         return i18n("Psion 8-bit A-law");
0317     else if( ext == "raw" )
0318         return i18n("Raw");
0319     else
0320         return i18n("Error");
0321 }
0322 
0323 
0324 long long K3bSoxEncoder::fileSize( const QString&, const K3b::Msf& msf ) const
0325 {
0326     // for now we make a rough assumption based on the settings
0327     KSharedConfig::Ptr c = KSharedConfig::openConfig();
0328     KConfigGroup grp(c, QStringLiteral("K3bSoxEncoderPlugin") );
0329     if( grp.readEntry( "manual settings", DEFAULT_MANUAL_SETTINGS ) ) {
0330         int sr =  grp.readEntry( "samplerate", DEFAULT_SAMPLE_RATE );
0331         int ch = grp.readEntry( "channels", DEFAULT_CHANNELS );
0332         int wsize = grp.readEntry( "data size", DEFAULT_DATA_SIZE );
0333 
0334         return msf.totalFrames()*sr*ch*wsize/75;
0335     }
0336     else {
0337         // fallback to raw
0338         return msf.audioBytes();
0339     }
0340 }
0341 
0342 #include "k3bsoxencoder.moc"
0343 
0344 #include "moc_k3bsoxencoder.cpp"