Add accuraterip-checksum program

This commit is contained in:
Merlijn Wajer
2016-01-19 20:07:13 -08:00
parent 2983fc3d03
commit d1f0165cf3
8 changed files with 286 additions and 1 deletions

4
.gitignore vendored
View File

@@ -14,6 +14,10 @@ missing
morituri.spec
py-compile
REVISION
*.o
.deps
/compile
/depcomp
# For Python development using Eclipse IDE
.project

View File

@@ -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

View File

@@ -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
)

View File

@@ -4,5 +4,6 @@ morituridir = $(PYTHONLIBDIR)/morituri/program
morituri_PYTHON = \
__init__.py \
arc.py \
cdparanoia.py \
cdrdao.py

1
src/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
accuraterip-checksum

4
src/Makefile.am Normal file
View File

@@ -0,0 +1,4 @@
bin_PROGRAMS = accuraterip-checksum
accuraterip_checksum_SOURCES = accuraterip-checksum.c
accuraterip_checksum_CFLAGS = -std=c99
accuraterip_checksum_LDADD = -lsndfile

43
src/README.md Normal file
View File

@@ -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

227
src/accuraterip-checksum.c Normal file
View File

@@ -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 <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <sndfile.h>
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;
}