DSF2FLAC
main.cpp
00001 /*
00002  * dsf2flac - http://code.google.com/p/dsf2flac/
00003  *
00004  * A file conversion tool for translating dsf dsd audio files into
00005  * flac pcm audio files.
00006  *
00007  * Copyright (c) 2013 by respective authors.
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  *
00023  *
00024  * Acknowledgements
00025  *
00026  * Many thanks to the following authors and projects whose work has greatly
00027  * helped the development of this tool.
00028  *
00029  *
00030  * Sebastian Gesemann - dsd2pcm (http://code.google.com/p/dsd2pcm/)
00031  * SACD Ripper (http://code.google.com/p/sacd-ripper/)
00032  * Maxim V.Anisiutkin - foo_input_sacd (http://sourceforge.net/projects/sacddecoder/files/)
00033  * Vladislav Goncharov - foo_input_sacd_hq (http://vladgsound.wordpress.com)
00034  * Jesus R - www.sonore.us
00035  *
00036  */
00037 
00038 #include <boost/timer/timer.hpp>
00039 #include <boost/filesystem.hpp>
00040 #include <dsd_decimator.h>
00041 #include <dsf_file_reader.h>
00042 #include <dsdiff_file_reader.h>
00043 #include <tagConversion.h>
00044 #include "FLAC++/metadata.h"
00045 #include "FLAC++/encoder.h"
00046 #include "math.h"
00047 #include "cmdline.h"
00048 #include <sstream>
00049 #include "dop_packer.h"
00050 
00051 #define flacBlockLen 100
00052 
00053 using boost::timer::cpu_timer;
00054 using boost::timer::cpu_times;
00055 using boost::timer::nanosecond_type;
00056 
00057 static nanosecond_type reportInterval(100000000LL);
00058 static cpu_timer timer;
00059 static dsf2flac_float64 lastPos;
00060 
00066 void setupTimer(dsf2flac_float64 currPos)
00067 {
00068         timer = cpu_timer();
00069         lastPos = currPos;
00070 }
00071 
00077 void checkTimer(dsf2flac_float64 currPos, dsf2flac_float64 percent)
00078 {
00079         cpu_times const elapsed_times(timer.elapsed());
00080         nanosecond_type const elapsed(elapsed_times.system + elapsed_times.user);
00081         if (elapsed >= reportInterval) {
00082                 printf("\33[2K\r");
00083                 printf("Rate: %4.1fx\t",(currPos-lastPos)/elapsed*1000000000);
00084                 printf("Progress: %3.0f%%",percent);
00085                 fflush(stdout);
00086                 lastPos = currPos;
00087                 timer = cpu_timer();
00088         }
00089 }
00090 
00096 boost::filesystem::path muti_track_name_helper(boost::filesystem::path outpath, int n) {
00097         std::ostringstream prefix;
00098         prefix << "track ";
00099         prefix << n+1;
00100         prefix << " - ";
00101         boost::filesystem::path trackOutPath = outpath.parent_path() / (prefix.str() + outpath.filename().string());
00102         return trackOutPath;
00103 }
00104 
00111 int pcm_track_helper(
00112         boost::filesystem::path outpath,
00113         dsdDecimator* dec,
00114         int bits,
00115         dsf2flac_float64 scale,
00116         dsf2flac_float64 tpdfDitherPeakAmplitude,
00117         dsf2flac_float64 startPos,
00118         dsf2flac_float64 endPos,
00119         ID3_Tag id3tag)
00120 {
00121         
00122         if ( startPos > dec->getLength()-1 )
00123                 startPos = dec->getLength()-1;
00124                 
00125         if ( endPos > dec->getLength() )
00126                 endPos = dec->getLength();
00127         
00128         // flac vars
00129         bool ok = true;
00130         FLAC::Encoder::File encoder;
00131         FLAC__StreamEncoderInitStatus init_status;
00132         FLAC__StreamMetadata *metadata[2];
00133 
00134         // setup the encoder
00135         if(!encoder) {
00136                 fprintf(stderr, "ERROR: allocating encoder\n");
00137                 return 0;
00138         }
00139         ok &= encoder.set_verify(true);
00140         ok &= encoder.set_compression_level(5);
00141         ok &= encoder.set_channels(dec->getNumChannels());
00142         ok &= encoder.set_bits_per_sample(bits);
00143         ok &= encoder.set_sample_rate(dec->getOutputSampleRate());
00144         ok &= encoder.set_total_samples_estimate(endPos - startPos);
00145 
00146         // add tags and a padding block
00147         if(ok) {
00148                 metadata[0] = id3v2_to_flac( id3tag );
00149                 metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
00150                 metadata[1]->length = 2048; /* set the padding length */
00151                 ok = encoder.set_metadata(metadata, 2);
00152         }
00153         
00154         // initialize encoder
00155         if(ok) {
00156                 init_status = encoder.init(outpath.c_str());
00157                 if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
00158                         fprintf(stderr, "ERROR: initializing encoder: %s\n", FLAC__StreamEncoderInitStatusString[init_status]);
00159                         ok = false;
00160                 }
00161         }
00162 
00163         // return if there is a problem with any of the flac stuff.
00164         if (!ok)
00165                 return ok;
00166 
00167         // creep up to the start point.
00168         while (dec->getPosition() < startPos) {
00169                 dec->step();
00170         }
00171         // create a FLAC__int32 buffer to hold the samples as they are converted
00172         unsigned int bufferLen = dec->getNumChannels()*flacBlockLen;
00173         FLAC__int32* buffer = new FLAC__int32[bufferLen];
00174         // MAIN CONVERSION LOOP //
00175         while (dec->getPosition() <= endPos-flacBlockLen) {
00176                 dec->getSamples(buffer,bufferLen,scale,tpdfDitherPeakAmplitude);
00177                 if(!(ok = encoder.process_interleaved(buffer, flacBlockLen)))
00178                         fprintf(stderr, "   state: %s\n", encoder.get_state().resolved_as_cstring(encoder));
00179                 checkTimer(dec->getPositionInSeconds(),dec->getPositionAsPercent());
00180         }
00181         // delete the old buffer and make a new one with a single sample per chan
00182         delete[] buffer;
00183         buffer = new FLAC__int32[dec->getNumChannels()];
00184         // creep up to the end
00185         while (dec->getPosition() <= endPos) {
00186                 dec->getSamples(buffer,dec->getNumChannels(),scale,tpdfDitherPeakAmplitude);
00187                 if(!(ok = encoder.process_interleaved(buffer, 1)))
00188                         fprintf(stderr, "   state: %s\n", encoder.get_state().resolved_as_cstring(encoder));
00189                 checkTimer(dec->getPositionInSeconds(),dec->getPositionAsPercent());
00190         }
00191         delete[] buffer;
00192         // close the flac file
00193         ok &= encoder.finish();
00194         // report back to the user
00195         printf("\33[2K\r");
00196         printf("%3.1f%%\t",dec->getPositionAsPercent());
00197         if (ok) {
00198                 printf("Conversion completed sucessfully.\n");
00199         } else {
00200                 printf("\nError during conversion.\n");
00201                 fprintf(stderr, "encoding: %s\n", ok? "succeeded" : "FAILED");
00202                 fprintf(stderr, "   state: %s\n", encoder.get_state().resolved_as_cstring(encoder));
00203         }
00204         // free things
00205         FLAC__metadata_object_delete(metadata[0]);
00206         FLAC__metadata_object_delete(metadata[1]);
00207 
00208         return ok;
00209 }
00210 
00211 /*
00212  * do_pcm_conversion
00213  *
00214  * this function uses a dsdDecimator to do the conversion into PCM.
00215  */
00216 int do_pcm_conversion(
00217                 DsdSampleReader* dsr,
00218                 int fs,
00219                 int bits,
00220                 bool dither,
00221                 dsf2flac_float64 userScale,
00222                 boost::filesystem::path inpath,
00223                 boost::filesystem::path outpath
00224                 )
00225 {
00226 
00227         bool ok = true;
00228 
00229         // create decimator
00230         dsdDecimator dec(dsr,fs);
00231         if (!dec.isValid()) {
00232                 printf("%s\n",dec.getErrorMsg().c_str());
00233                 return 0;
00234         }
00235 
00236         // calc real scale and dither amplitude
00237         dsf2flac_float64 scale = userScale * pow(2.0,bits-1); // increase scale by factor of 2^23 (24bit).
00238         dsf2flac_float64 tpdfDitherPeakAmplitude;
00239         if (dither)
00240                 tpdfDitherPeakAmplitude = 1.0;
00241         else
00242                 tpdfDitherPeakAmplitude = 0.0;
00243 
00244         setupTimer(dsr->getPositionInSeconds());
00245 
00246         // convert each track in the file in turn
00247         for (dsf2flac_uint32 n = 0; n < dsr->getNumTracks();n++) {
00248 
00249                 // get and check the start and end samples
00250                 dsf2flac_float64 trackStart = (dsf2flac_float64)dsr->getTrackStart(n) / dec.getDecimationRatio();
00251                 dsf2flac_float64 trackEnd = (dsf2flac_float64)dsr->getTrackEnd(n) / dec.getDecimationRatio();
00252                 if (trackStart < dec.getFirstValidSample())
00253                         trackStart = dec.getFirstValidSample();
00254                 if (trackStart >= dec.getLastValidSample() )
00255                         trackStart = dec.getLastValidSample() - 1;
00256                 if (trackEnd <= dec.getFirstValidSample())
00257                         trackEnd = dec.getFirstValidSample() + 1;
00258                 if (trackEnd > dec.getLastValidSample())
00259                         trackEnd = dec.getLastValidSample();
00260 
00261                 // construct an appropriate filename for multi track files.
00262                 boost::filesystem::path trackOutPath;
00263                 if (dsr->getNumTracks() > 1) {
00264                         trackOutPath = muti_track_name_helper(outpath,n);
00265                 } else {
00266                         trackOutPath = outpath;
00267                 }
00268 
00269                 printf("Output file\n\t%s\n",trackOutPath.c_str());
00270                 // use the pcm_track_helper
00271                 ok &= pcm_track_helper(trackOutPath,&dec,bits,scale,tpdfDitherPeakAmplitude,trackStart,trackEnd,dsr->getID3Tag(n));
00272         }
00273 
00274         return ok;
00275 }
00276 
00280 int dop_track_helper(
00281         boost::filesystem::path outpath,
00282         DsdSampleReader* dsr,
00283         dsf2flac_int64 startPos,
00284         dsf2flac_int64 endPos,
00285         ID3_Tag id3tag)
00286 {
00287 
00288         // double check the start and end positions!
00289         if ( startPos > dsr->getLength()-1 )
00290                 startPos = dsr->getLength()-1;
00291         if ( endPos > dsr->getLength() )
00292                 endPos = dsr->getLength();
00293 
00294         // create a dop packer object.
00295         DopPacker dopp = DopPacker(dsr);
00296 
00297         // flac vars
00298         bool ok = true;
00299         FLAC::Encoder::File encoder;
00300         FLAC__StreamEncoderInitStatus init_status;
00301         FLAC__StreamMetadata *metadata[2];
00302 
00303         // setup the encoder
00304         if(!encoder) {
00305                 fprintf(stderr, "ERROR: allocating encoder\n");
00306                 return 0;
00307         }
00308         ok &= encoder.set_verify(true);
00309         ok &= encoder.set_compression_level(5);
00310         ok &= encoder.set_channels(dsr->getNumChannels());
00311         ok &= encoder.set_bits_per_sample(24);
00312 
00313         if ( dsr->getSamplingFreq() == 2822400 ) {
00314                 ok &= encoder.set_sample_rate(176400);
00315         } else if ( dsr->getSamplingFreq() == 5644800 ) {
00316                 ok &= encoder.set_sample_rate(352800);
00317         } else {
00318                 fprintf(stderr, "ERROR: sample rate not supported by DoP\n");
00319                 return 0;
00320         }
00321         ok &= encoder.set_total_samples_estimate((endPos - startPos)/16);
00322 
00323         // add tags and a padding block
00324         if(ok) {
00325                 metadata[0] = id3v2_to_flac( id3tag );
00326                 metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
00327                 metadata[1]->length = 2048; /* set the padding length */
00328                 ok = encoder.set_metadata(metadata, 2);
00329         }
00330 
00331         // initialize encoder
00332         if(ok) {
00333                 init_status = encoder.init(outpath.c_str());
00334                 if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
00335                         fprintf(stderr, "ERROR: initializing encoder: %s\n", FLAC__StreamEncoderInitStatusString[init_status]);
00336                         ok = false;
00337                 }
00338         }
00339 
00340         // return if there is a problem with any of the flac stuff.
00341         if (!ok)
00342                 return ok;
00343 
00344         // creep up to the start point.
00345         while (dsr->getPosition() < startPos) {
00346                 dsr->step();
00347         }
00348         // create a FLAC__int32 buffer to hold the samples as they are converted
00349         unsigned int bufferLen = dsr->getNumChannels()*flacBlockLen;
00350         FLAC__int32* buffer = new FLAC__int32[bufferLen];
00351         // MAIN CONVERSION LOOP //
00352         while (dsr->getPosition() <= endPos-flacBlockLen*16) {
00353                 dopp.pack_buffer(buffer,bufferLen);
00354                 if(!(ok = encoder.process_interleaved(buffer, flacBlockLen)))
00355                         fprintf(stderr, "   state: %s\n", encoder.get_state().resolved_as_cstring(encoder));
00356                 checkTimer(dsr->getPositionInSeconds(),dsr->getPositionAsPercent());
00357         }
00358         // delete the old buffer and make a new one with a single sample per chan
00359         delete[] buffer;
00360         buffer = new FLAC__int32[dsr->getNumChannels()];
00361         // creep up to the end
00362         while (dsr->getPosition() <= endPos) {
00363                 dopp.pack_buffer(buffer,dsr->getNumChannels());
00364                 if(!(ok = encoder.process_interleaved(buffer, 1)))
00365                         fprintf(stderr, "   state: %s\n", encoder.get_state().resolved_as_cstring(encoder));
00366                 checkTimer(dsr->getPositionInSeconds(),dsr->getPositionAsPercent());
00367         }
00368         delete[] buffer;
00369         // close the flac file
00370         ok &= encoder.finish();
00371         // report back to the user
00372         printf("\33[2K\r");
00373         printf("%3.1f%%\t",dsr->getPositionAsPercent());
00374         if (ok) {
00375                 printf("Conversion completed sucessfully.\n");
00376         } else {
00377                 printf("\nError during conversion.\n");
00378                 fprintf(stderr, "encoding: %s\n", ok? "succeeded" : "FAILED");
00379                 fprintf(stderr, "   state: %s\n", encoder.get_state().resolved_as_cstring(encoder));
00380         }
00381         // free things
00382         FLAC__metadata_object_delete(metadata[0]);
00383         FLAC__metadata_object_delete(metadata[1]);
00384 
00385         return ok;
00386 
00387 
00388         return ok;
00389 }
00396 int do_dop_conversion(
00397                 DsdSampleReader* dsr,
00398                 boost::filesystem::path inpath,
00399                 boost::filesystem::path outpath
00400                 )
00401 {
00402         bool ok = true;
00403 
00404         setupTimer(dsr->getPositionInSeconds());
00405 
00406         // convert each track in the file in turn
00407         for (dsf2flac_uint32 n = 0; n < dsr->getNumTracks();n++) {
00408 
00409                 // get and check the start and end samples
00410                 dsf2flac_uint64 trackStart = dsr->getTrackStart(n);
00411                 dsf2flac_uint64 trackEnd = dsr->getTrackEnd(n);
00412 
00413                 // construct an appropriate filename for multi track files.
00414                 boost::filesystem::path trackOutPath;
00415                 if (dsr->getNumTracks() > 1) {
00416                         trackOutPath = muti_track_name_helper(outpath,n);
00417                 } else {
00418                         trackOutPath = outpath;
00419                 }
00420 
00421                 printf("Output file\n\t%s\n",trackOutPath.c_str());
00422                 // use the pcm_track_helper
00423                 ok &= dop_track_helper(trackOutPath,dsr,trackStart,trackEnd,dsr->getID3Tag(n));
00424         }
00425 
00426         return ok;
00427 }
00428 
00434 int main(int argc, char **argv)
00435 {
00436         // use the cmdline processor
00437         gengetopt_args_info args_info;
00438         if (cmdline_parser (argc, argv, &args_info) != 0)
00439                 exit(1) ;
00440 
00441         // if help or version given then exit now.
00442         if (args_info.help_given || args_info.version_given)
00443                 exit(1);
00444 
00445         // collect the options
00446         int fs = args_info.samplerate_arg;
00447         int bits = args_info.bits_arg;
00448         bool dither = !args_info.nodither_flag;
00449         bool dop = args_info.dop_flag;
00450         dsf2flac_float64 userScaleDB = (dsf2flac_float64) args_info.scale_arg;
00451         dsf2flac_float64 userScale = pow(10.0,userScaleDB/20);
00452         boost::filesystem::path inpath(args_info.infile_arg);
00453         boost::filesystem::path outpath;
00454         if (args_info.outfile_given)
00455                 outpath = args_info.outfile_arg;
00456         else {
00457                 outpath = inpath;
00458                 outpath.replace_extension(".flac");
00459         }
00460 
00461         printf("%s ",CMDLINE_PARSER_PACKAGE_NAME);
00462         printf("%s\n\n",CMDLINE_PARSER_VERSION);
00463 
00464         // pointer to the dsdSampleReader (could be any valid type).
00465         DsdSampleReader* dsr;
00466 
00467         // create either a reader for dsf or dsd
00468         if (inpath.extension() == ".dsf" || inpath.extension() == ".DSF")
00469                 dsr = new dsfFileReader((char*)inpath.c_str());
00470         else if (inpath.extension() == ".dff" || inpath.extension() == ".DFF")
00471                 dsr = new dsdiffFileReader((char*)inpath.c_str());
00472         else {
00473                 printf("Sorry, only .dff or .dff input files are supported\n");
00474                 return 0;
00475         }
00476 
00477         // check reader is valid.
00478         if (!dsr->isValid()) {
00479                 printf("Error opening DSDFF file!\n");
00480                 printf("%s\n",dsr->getErrorMsg().c_str());
00481                 return 0;
00482         }
00483         
00484         // do the conversion into PCM or DoP
00485         if (!dop) {
00486                 // feedback some info to the user
00487                 printf("Input file\n\t%s\n",inpath.c_str());
00488                 printf("Output format\n\tSampleRate: %dHz\n\tDepth: %dbit\n\tDither: %s\n\tScale: %1.1fdB\n",fs, bits, (dither)?"true":"false",userScaleDB);
00489                 //printf("\tIdleSample: 0x%02x\n",dsr->getIdleSample());
00490 
00491                 return do_pcm_conversion(dsr,fs,bits,dither,userScale,inpath,outpath);
00492         } else {
00493                 // feedback some info to the user
00494                 printf("Input file\n\t%s\n",inpath.c_str());
00495                 printf("Output format\n\tDSD samples packed as DoP\n");
00496 
00497                 return do_dop_conversion(dsr,inpath,outpath);
00498         }
00499 }
 All Classes Files Functions Variables