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"