diff --git a/.gitignore b/.gitignore index cd47e21..10502f3 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,10 @@ missing morituri.spec py-compile REVISION +*.o +.deps +/compile +/depcomp # For Python development using Eclipse IDE .project diff --git a/Makefile.am b/Makefile.am index 8680af5..7a7e846 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ AUTOMAKE_OPTIONS = 1.8 dist-bzip2 no-dist-gzip ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = morituri bin etc doc m4 misc +SUBDIRS = morituri bin etc doc m4 misc src EXTRA_DIST = morituri.spec morituri.doap RELEASE README.md HACKING REVISION diff --git a/configure.ac b/configure.ac index a89c06b..a64eb2c 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,10 @@ dnl check for pychecker AC_CHECK_PROG(PYCHECKER, pychecker, yes, no) AM_CONDITIONAL(HAVE_PYCHECKER, test "x$PYCHECKER" = "xyes") +dnl +AC_PROG_CC +AC_CHECK_LIB(sndfile,sf_open) + dnl output stuff AC_CONFIG_FILES([bin/rip], [chmod +x bin/rip]) AC_CONFIG_FILES([etc/bash_completion.d/bash-compgen], @@ -81,4 +85,5 @@ morituri/test/Makefile doc/Makefile misc/Makefile morituri.spec +src/Makefile ) diff --git a/morituri/program/Makefile.am b/morituri/program/Makefile.am index dc15eb2..a42ccab 100644 --- a/morituri/program/Makefile.am +++ b/morituri/program/Makefile.am @@ -4,5 +4,6 @@ morituridir = $(PYTHONLIBDIR)/morituri/program morituri_PYTHON = \ __init__.py \ + arc.py \ cdparanoia.py \ cdrdao.py diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..84cb642 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +accuraterip-checksum diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ce794b1 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,4 @@ +bin_PROGRAMS = accuraterip-checksum +accuraterip_checksum_SOURCES = accuraterip-checksum.c +accuraterip_checksum_CFLAGS = -std=c99 +accuraterip_checksum_LDADD = -lsndfile diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..c261771 --- /dev/null +++ b/src/README.md @@ -0,0 +1,43 @@ +accuraterip-checksum +==================== + +# Description: +A C99 commandline program to compute the AccurateRip checksum of singletrack WAV files. +Implemented according to + + http://www.hydrogenaudio.org/forums/index.php?showtopic=97603 + +# Syntax: + accuraterip-checksum [--version / --accuraterip-v1 / --accuraterip-v2 (default)] filename track_number total_tracks + +# Output: +By default, the V2 (AccurateRip version 2) checksum will be printed. +You can also obtain the V1 checksum with the "--accuraterip-v1" parameter. + +You can obtain the version of accuraterip-checksum using the "--version" parameter. This is not to be confused with the AccurateRip version! + +The version of accuraterip-checksum should be added to audio files which are tagged using the output of accuraterip-checksum. If any severe bugs are ever found in accuraterip-checksum, this will allow you to identify files which were tagged using affected version. + + +# Compiling: +libsndfile is used for reading the WAV files. +Therefore, on Ubuntu 12.04, make sure you have the following packages installed: + + libsndfile1 (should be installed by default) + libsndfile1-dev + +The configuration files of an Eclipse project are included. +You can use "EGit" (Eclipse git) to import the whole repository. +It should as well ask you to import the project configuration then. + +# Author: +Leo Bogert (http://leo.bogert.de) + +# Version: +1.4 + +# Donations: + bitcoin:14kPd2QWsri3y2irVFX6wC33vv7FqTaEBh + +# License: +GPLv3 diff --git a/src/accuraterip-checksum.c b/src/accuraterip-checksum.c new file mode 100644 index 0000000..5d7b476 --- /dev/null +++ b/src/accuraterip-checksum.c @@ -0,0 +1,227 @@ +/* + ============================================================================ + Name : accuraterip-checksum.c + Author : Leo Bogert (http://leo.bogert.de) + Git : http://leo.bogert.de/accuraterip-checksum + Version : See global variable "version" + Copyright : GPL + Description : A C99 commandline program to compute the AccurateRip checksum of singletrack WAV files. + Implemented according to http://www.hydrogenaudio.org/forums/index.php?showtopic=97603 + ============================================================================ + */ + +#include +#include +#include +#include +#include +#include +#include + +const char *const version = "1.4"; + +bool check_fileformat(const SF_INFO* sfinfo) { +#ifdef DEBUG + printf("Channels: %i\n", sfinfo->channels); + printf("Format: %X\n", sfinfo->format); + printf("Frames: %li\n", sfinfo->frames); + printf("Samplerate: %i\n", sfinfo->samplerate); + printf("Sections: %i\n", sfinfo->sections); + printf("Seekable: %i\n", sfinfo->seekable); +#endif + + if(sfinfo->channels != 2) return false; + if((sfinfo->format & SF_FORMAT_TYPEMASK & SF_FORMAT_WAV) != SF_FORMAT_WAV) return false; + if((sfinfo->format & SF_FORMAT_SUBMASK & SF_FORMAT_PCM_16) != SF_FORMAT_PCM_16) return false; + //if((sfinfo->format & SF_FORMAT_ENDMASK & SF_ENDIAN_LITTLE) != SF_ENDIAN_LITTLE) return false; + if(sfinfo->samplerate != 44100) return false; + + return true; +} + +size_t get_full_audiodata_size(const SF_INFO* sfinfo) { + // 16bit = samplesize, 8 bit = bitcount in byte + return sfinfo->frames * sfinfo->channels * (16 / 8); +} + +uint32_t* load_full_audiodata(SNDFILE* sndfile, const SF_INFO* sfinfo) { + uint32_t* data = (uint32_t*)malloc(get_full_audiodata_size(sfinfo)); + + if(data == NULL) + return NULL; + + if(sf_readf_short(sndfile, (short*)data, sfinfo->frames) != sfinfo->frames) { + free(data); + return NULL; + } + + return data; +} + +uint32_t compute_v1_checksum(const uint32_t* audio_data, const size_t audio_data_size, const int track_number, const int total_tracks) { +#define DWORD uint32_t + + const DWORD *pAudioData = audio_data; // this should point entire track audio data + int DataSize = audio_data_size; // size of the data + int TrackNumber = track_number; // actual track number on disc, note that for the first & last track the first and last 5 sectors are skipped + int AudioTrackCount = total_tracks; // CD track count + + //---------AccurateRip CRC checks------------ + DWORD AR_CRC = 0, AR_CRCPosMulti = 1; + DWORD AR_CRCPosCheckFrom = 0; + DWORD AR_CRCPosCheckTo = DataSize / sizeof(DWORD); +#define SectorBytes 2352 // each sector + if (TrackNumber == 1) // first? + AR_CRCPosCheckFrom+= ((SectorBytes * 5) / sizeof(DWORD)); + if (TrackNumber == AudioTrackCount) // last? + AR_CRCPosCheckTo-=((SectorBytes * 5) / sizeof(DWORD)); + + + int DataDWORDSize = DataSize / sizeof(DWORD); + for (int i = 0; i < DataDWORDSize; i++) + { + if (AR_CRCPosMulti >= AR_CRCPosCheckFrom && AR_CRCPosMulti <= AR_CRCPosCheckTo) + AR_CRC+=(AR_CRCPosMulti * pAudioData[i]); + + AR_CRCPosMulti++; + } + + return AR_CRC; +} + +uint32_t compute_v2_checksum(const uint32_t* audio_data, const size_t audio_data_size, const int track_number, const int total_tracks) { +#define DWORD uint32_t +#define __int64 uint64_t + + const DWORD *pAudioData = audio_data; // this should point entire track audio data + int DataSize = audio_data_size; // size of the data + int TrackNumber = track_number; // actual track number on disc, note that for the first & last track the first and last 5 sectors are skipped + int AudioTrackCount = total_tracks; // CD track count + + //---------AccurateRip CRC checks------------ + DWORD AR_CRCPosCheckFrom = 0; + DWORD AR_CRCPosCheckTo = DataSize / sizeof(DWORD); +#define SectorBytes 2352 // each sector + if (TrackNumber == 1) // first? + AR_CRCPosCheckFrom+= ((SectorBytes * 5) / sizeof(DWORD)); + if (TrackNumber == AudioTrackCount) // last? + AR_CRCPosCheckTo-=((SectorBytes * 5) / sizeof(DWORD)); + + int DataDWORDSize = DataSize / sizeof(DWORD); + + DWORD AC_CRCNEW = 0; + DWORD MulBy = 1; + for (int i = 0; i < DataDWORDSize; i++) + { + if (MulBy >= AR_CRCPosCheckFrom && MulBy <= AR_CRCPosCheckTo) + { + DWORD Value = pAudioData[i]; + + uint64_t CalcCRCNEW = (uint64_t)Value * (uint64_t)MulBy; + DWORD LOCalcCRCNEW = (DWORD)(CalcCRCNEW & (uint64_t)0xFFFFFFFF); + DWORD HICalcCRCNEW = (DWORD)(CalcCRCNEW / (uint64_t)0x100000000); + AC_CRCNEW+=HICalcCRCNEW; + AC_CRCNEW+=LOCalcCRCNEW; + } + MulBy++; + } + + return AC_CRCNEW; +} + +void print_syntax_to_stderr() { + fprintf(stderr, "Syntax: accuraterip-checksum [--version / --accuraterip-v1 / --accuraterip-v2 (default)] filename track_number total_tracks\n"); +} + +int main(int argc, const char** argv) { + int arg_offset; + bool use_v1; + + switch(argc) { + case 2: + if(strcmp(argv[1], "--version") != 0) { + print_syntax_to_stderr(); + return EXIT_FAILURE; + } + printf("accuraterip-checksum version %s\n", version); + return EXIT_SUCCESS; + case 4: + arg_offset = 0; + use_v1 = false; + break; + case 5: + arg_offset = 1; + if(!strcmp(argv[1], "--accuraterip-v1")) { + use_v1 = true; + } else if(!strcmp(argv[1], "--accuraterip-v2")) { + use_v1 = false; + } else { + print_syntax_to_stderr(); + return EXIT_FAILURE; + } + break; + default: + print_syntax_to_stderr(); + return EXIT_FAILURE; + } + + const char* filename = argv[1 + arg_offset]; + const char* track_number_string = argv[2 + arg_offset]; + const char* total_tracks_string = argv[3 + arg_offset]; + + const int track_number = atoi(track_number_string); + const int total_tracks = atoi(total_tracks_string); + + if(track_number < 1 || track_number > total_tracks) { + fprintf(stderr, "Invalid track_number!\n"); + return EXIT_FAILURE; + } + + if(total_tracks < 1 || total_tracks > 99) { + fprintf(stderr, "Invalid total_tracks!\n"); + return EXIT_FAILURE; + } + +#ifdef DEBUG + printf("Reading %s\n", filename); +#endif + + SF_INFO sfinfo; + sfinfo.channels = 0; + sfinfo.format = 0; + sfinfo.frames = 0; + sfinfo.samplerate = 0; + sfinfo.sections = 0; + sfinfo.seekable = 0; + + SNDFILE* sndfile = sf_open(filename, SFM_READ, &sfinfo); + + if(sndfile == NULL) { + fprintf(stderr, "sf_open failed! sf_error==%i\n", sf_error(NULL)); + return EXIT_FAILURE; + } + + if(!check_fileformat(&sfinfo)) { + fprintf(stderr, "check_fileformat failed!\n"); + sf_close(sndfile); + return EXIT_FAILURE; + } + + uint32_t* audio_data = load_full_audiodata(sndfile, &sfinfo); + if(audio_data == NULL) { + fprintf(stderr, "load_full_audiodata failed!\n"); + sf_close(sndfile); + return EXIT_FAILURE; + } + + const int checksum = use_v1 ? + compute_v1_checksum(audio_data, get_full_audiodata_size(&sfinfo), track_number, total_tracks) + : compute_v2_checksum(audio_data, get_full_audiodata_size(&sfinfo), track_number, total_tracks); + + printf("%08X\n", checksum); + + sf_close(sndfile); + free(audio_data); + + return EXIT_SUCCESS; +}