Files
whipper-gui/src/accuraterip-checksum.c
Andreas Oberritter 8446c290e7 accuraterip-checksum.c: Port to Python 3
Accuraterip-checksum extension will be Python 3 only (JoeLametta).

Co-authored-by: JoeLametta <JoeLametta@users.noreply.github.com>
Signed-off-by: Andreas Oberritter <obi@saftware.de>
Signed-off-by: JoeLametta <JoeLametta@users.noreply.github.com>
2019-11-26 18:46:12 +00:00

160 lines
4.2 KiB
C

/*
============================================================================
Name : accuraterip-checksum.c
Authors : Leo Bogert (http://leo.bogert.de), Andreas Oberritter
License : GPLv3
Description : A Python C extension to compute the AccurateRip checksum of WAV or FLAC tracks.
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>
#include <Python.h>
static 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
switch (sfinfo->format & SF_FORMAT_TYPEMASK) {
case SF_FORMAT_WAV:
case SF_FORMAT_FLAC:
return (sfinfo->channels == 2) &&
(sfinfo->samplerate == 44100) &&
((sfinfo->format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_16);
}
return false;
}
static void *load_full_audiodata(SNDFILE *sndfile, const SF_INFO *sfinfo, size_t size)
{
void *data = malloc(size);
if(data == NULL)
return NULL;
if(sf_readf_short(sndfile, data, sfinfo->frames) != sfinfo->frames) {
free(data);
return NULL;
}
return data;
}
static void compute_checksums(const uint32_t *audio_data, size_t audio_data_size, size_t track_number, size_t total_tracks, uint32_t *v1, uint32_t *v2)
{
uint32_t csum_hi = 0;
uint32_t csum_lo = 0;
uint32_t AR_CRCPosCheckFrom = 0;
size_t Datauint32_tSize = audio_data_size / sizeof(uint32_t);
uint32_t AR_CRCPosCheckTo = Datauint32_tSize;
const size_t SectorBytes = 2352; // each sector
uint32_t MulBy = 1;
size_t i;
if (track_number == 1) // first?
AR_CRCPosCheckFrom += ((SectorBytes * 5) / sizeof(uint32_t));
if (track_number == total_tracks) // last?
AR_CRCPosCheckTo -= ((SectorBytes * 5) / sizeof(uint32_t));
for (i = 0; i < Datauint32_tSize; i++) {
if (MulBy >= AR_CRCPosCheckFrom && MulBy <= AR_CRCPosCheckTo) {
uint64_t product = (uint64_t)audio_data[i] * (uint64_t)MulBy;
csum_hi += (uint32_t)(product >> 32);
csum_lo += (uint32_t)(product);
}
MulBy++;
}
*v1 = csum_lo;
*v2 = csum_lo + csum_hi;
}
static PyObject *accuraterip_compute(PyObject *self, PyObject *args)
{
const char *filename;
unsigned int track_number;
unsigned int total_tracks;
uint32_t v1, v2;
void *audio_data;
size_t size;
SF_INFO sfinfo;
SNDFILE *sndfile = NULL;
if (!PyArg_ParseTuple(args, "sII", &filename, &track_number, &total_tracks))
goto err;
if (track_number < 1 || track_number > total_tracks) {
fprintf(stderr, "Invalid track_number!\n");
goto err;
}
if (total_tracks < 1 || total_tracks > 99) {
fprintf(stderr, "Invalid total_tracks!\n");
goto err;
}
#ifdef DEBUG
printf("Reading %s\n", filename);
#endif
memset(&sfinfo, 0, sizeof(sfinfo));
sndfile = sf_open(filename, SFM_READ, &sfinfo);
if (sndfile == NULL) {
fprintf(stderr, "sf_open failed! sf_error==%i\n", sf_error(NULL));
goto err;
}
if (!check_fileformat(&sfinfo)) {
fprintf(stderr, "check_fileformat failed!\n");
goto err;
}
size = sfinfo.frames * sfinfo.channels * sizeof(uint16_t);
audio_data = load_full_audiodata(sndfile, &sfinfo, size);
if (audio_data == NULL) {
fprintf(stderr, "load_full_audiodata failed!\n");
goto err;
}
compute_checksums(audio_data, size, track_number, total_tracks, &v1, &v2);
free(audio_data);
sf_close(sndfile);
return Py_BuildValue("II", v1, v2);
err:
if (sndfile)
sf_close(sndfile);
return Py_BuildValue("OO", Py_None, Py_None);
}
static PyMethodDef accuraterip_methods[] = {
{ "compute", accuraterip_compute, METH_VARARGS, "Compute AccurateRip v1 and v2 checksums" },
{ NULL, NULL, 0, NULL },
};
static struct PyModuleDef accuraterip_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "accuraterip",
.m_methods = accuraterip_methods,
};
PyMODINIT_FUNC PyInit_accuraterip(void)
{
return PyModule_Create(&accuraterip_module);
}