DSF2FLAC
|
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 }