/* dab.c - Decode Aiken Biphase
Copyright (c) 2004-2005 Joseph Battaglia <redbird@2600.com>
Released under the MIT License.
Compiling
cc dab.c -o dab -lsndfile
Translator's Note:
This code was printed with an article called "Magnetic Stripe Reading" by
Redbird in 2600 Volume 22, Number 1 (Spring 2005).
It is useful for decoding Magstripe using a computer sound card.
I have tested it with a microphone and ensured the accuracy of this
reproduction to a fair degree. I have not tried it with a magnetic sensor yet.
Since it was released under the MIT License, I have modified it very slightly
and am re-releasing it.
Download it here: https://altsci.com/concepts/dab.c
*/
#include <fcntl.h>
#include <getopt.h>
#include <sndfile.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define DEVICE "/dev/dsp" /* default sound card device */
#define SAMPLE_RATE 192000
#define SILENCE_THRES 5000
/* #define DISABLE_VC */
#define AUTO_THRES 30
#define BUF_SIZE 1024
#define END_LENGTH 200
#define FREQ_THRES 60
#define MAX_TERM 60
#define VERSION "0.6"
short int *sample = NULL;
int sample_size = 0;
void *xmalloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if (ptr == NULL) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
return ptr;
}
void *xrealloc(void *ptr, size_t size)
{
void *nptr;
nptr = realloc(ptr, size);
if (nptr == NULL) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
return nptr;
}
char *xstrdup(char *string)
{
char *ptr;
ptr = xmalloc(strlen(string) + 1);
strcpy(ptr, string);
return ptr;
}
ssize_t xread(int fd, void *buf, size_t count)
{
int retval;
retval = read(fd, buf, count);
if (retval == -1) {
perror("read()");
exit(EXIT_FAILURE);
}
return retval;
}
/* prints version
[stream] output stream */
void print_version(FILE *stream)
{
fprintf(stream, "dab - Decode Aiken Biphase\n");
fprintf(stream, "Version %s\n", VERSION);
fprintf(stream, "Copyright (c) 2004-2005 ");
fprintf(stream, "Joseph Battaglia <redbird@2600.com>\n");
}
/* prints version and help
[stream] output stream
[exec] string containing the name of the executable */
void print_help(FILE *stream, char *exec)
{
print_version(stream);
fprintf(stream, "Usage: %s [OPTIONS]\n\n", exec);
fprintf(stream, " -a, --auto-thres Set auto-thres percentage\n");
fprintf(stream, " (default: %d)\n", AUTO_THRES);
fprintf(stream, " -d, --device Device to read audio data from\n");
fprintf(stream, " (default: %s)\n", DEVICE);
fprintf(stream, " -f, --file File to read audio data from\n");
fprintf(stream, " (use instead of -d)\n");
fprintf(stream, " -h, --help Print help information\n");
fprintf(stream, " -m, --max-level Show maximum level\n");
fprintf(stream, " (use to determine threshold)\n");
fprintf(stream, " -s, --silent No verbose messages\n");
fprintf(stream, " -t, --device Set silence threshold\n");
fprintf(stream, " (default: automatic detect)\n");
fprintf(stream, " -v, --version Print version information\n");
}
/* sets the device parameters
[fd]
[verbose]
returns sample rate */
int dsp_init(int fd, int verbose)
{
int ch, fmt, sr;
if (verbose)
fprintf(stderr, "*** setting audio device parameters:\n");
if (verbose)
fprintf(stderr, " Format: AFMT_S16_LE\n");
fmt= AFMT_S16_LE;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) == -1) {
perror("SNDCTL_DSP_SETFMT");
exit(EXIT_FAILURE);
}
if (verbose)
fprintf(stderr, " Channels: 1\n");
ch = 0;
if (ioctl(fd, SNDCTL_DSP_STEREO, &ch) == -1) {
perror("SNDCTL_DSP_STEREO");
exit(EXIT_FAILURE);
}
if (verbose)
fprintf(stderr, " Sample rate: %i\n", SAMPLE_RATE);
sr = SAMPLE_RATE;
if (ioctl(fd, SNDCTL_DSP_SPEED, &sr) == -1) {
perror("SNDCTL_DSP_SPEED");
exit(EXIT_FAILURE);
}
if (sr != SAMPLE_RATE)
fprintf(stderr, "*** Warning: Highest supported sample rate is %d\n", sr);
return sr;
}
/* prints the maximum dsp level to aid in setting the silence
[fd]
[sample_rate] sample rate of device */
void print_max_level(int fd, int sample_rate)
{
int i;
short int buf, last = 0;
printf("Terminating after %d seconds...\n", MAX_TERM);
for (i=0; i< sample_rate * MAX_TERM; i++) {
xread(fd, &buf, sizeof (short int));
if (buf < 0)
buf = -buf;
if (buf > last) {
printf("Maximum level: %d\r", buf);
fflush(stdout);
last = buf;
}
}
printf("\n");
}
/* find the maximum value in sample
** global **
[sample] sample
[sample_size] number of frames in sample */
short int evaluate_max(void)
{
int i;
short int max = 0;
for (i=0; i < sample_size; i++) {
if (sample[i] > max)
max = sample[i];
}
return max;
}
/* pauses until the dsp is above the silence threshold
[fd]
[silence_thres] silence threshold */
void silence_pause(int fd, int silence_thres)
{
short int buf = 0;
while (buf < silence_thres) {
xread(fd, &buf, sizeof (short int));
if (buf < 0)
buf = -buf;
}
}
/* gets a sample, terminating when the input goes below the silence threshold
[fd]
[sample_rate] sample rate of device
[silence_thres] silence threshold
** global **
[sample] sample
[sample_size] number of frames in sample */
void get_dsp(int fd, int sample_rate, int silence_thres)
{
int count = 0, eos = 0, i;
short buf;
sample_size = 0;
silence_pause(fd, silence_thres);
while (!eos) {
sample = xrealloc(sample, sizeof (short int) * (BUF_SIZE * (count + 1)));
for (i = 0; i < BUF_SIZE; i++) {
xread(fd, &buf, sizeof (short int));
sample[i + (count * BUF_SIZE)] = buf;
}
count++;
sample_size = count * BUF_SIZE;
eos = 1;
if (sample_size > (sample_rate * END_LENGTH) / 1000) {
for (i = 0; i < (sample_rate * END_LENGTH) / 1000; i++) {
buf = sample[(count * BUF_SIZE) - i];
if (buf < 0)
buf = -buf;
if (buf > silence_thres)
eos = 0;
}
} else
eos = 0;
}
}
/* open the file
[fd]
[verbose]
** global **
[sample_size] number of frames in the file */
SNDFILE *sndfile_init(int fd, int verbose)
{
SNDFILE *sndfile;
SF_INFO sfinfo;
memset(&sfinfo, 0, sizeof(sfinfo));
sndfile = sf_open_fd(fd, SFM_READ, &sfinfo, 0);
if (sndfile == NULL) {
fprintf(stderr, "*** Error: sf_open_fd() failed\n");
exit(EXIT_FAILURE);
}
if (verbose) {
fprintf(stderr, "*** Input file format:\n"
" Frames: %i\n"
" Sample Rate: %i\n"
" Channels: %i\n"
" Format: 0x%08x\n"
" Sections: %i\n"
" Seekable: %i\n",
(int)sfinfo.frames, sfinfo.samplerate, sfinfo.channels,
sfinfo.format, sfinfo.sections, sfinfo.seekable);
}
if (sfinfo.channels != 1) {
fprintf(stderr, "*** Error: Only monaural files are supported\n");
exit(EXIT_FAILURE);
}
sample_size = sfinfo.frames;
return sndfile;
}
/* read in data from libsndfile
[sndfile]
** global **
[sample] sample
[sample_size] number of frames in sample */
void get_sndfile(SNDFILE *sndfile)
{
sf_count_t count;
sample = xmalloc(sizeof(short int) * sample_size);
count = sf_read_short(sndfile, sample, sample_size);
if (count != sample_size) {
fprintf(stderr, "*** Warning, expected %i frames, read %i\n",
sample_size, (int)count);
sample_size = count;
}
}
/* decodes aiken biphase and prints binary
[freq_thres] frequency threshold
** global **
[sample] sample
[sample_size] number of frames in sample */
void decode_aiken_biphase(int freq_thres, int silence_thres)
{
int i = 0, peak = 0, ppeak = 0;
int *peaks = NULL, peaks_size = 0;
int zerobl;
printf("*** Parsing %d samples\n", sample_size);
for (i=0; i < sample_size; i++) /* absolute value */
if (sample[i] < 0)
sample[i] = -sample[i];
i = 0; /* store peak differences */
while (i < sample_size) {
ppeak = peak; /* old peak value */
while (sample[i] <= silence_thres && i < sample_size)
i++;
peak = 0;
while (sample[i] > silence_thres && i < sample_size) {
if (sample[i] > sample[peak])
peak = i;
i++;
}
if (peak - ppeak > 0) {
peaks = xrealloc(peaks, sizeof(int) * (peaks_size + 1));
peaks[peaks_size] = peak - ppeak;
peaks_size++;
}
}
printf("Found %d peaks\n", peaks_size);
/* decode aiken biphase allowing for
frequency deviation based on freq_thres */
/* ignore first two peaks and last peak */
zerobl = peaks[2];
for (i = 2; i < peaks_size - 1; i++) {
if (peaks[i] < ((zerobl / 2) + (freq_thres * (zerobl / 2) / 100)) &&
peaks[i] > ((zerobl / 2) - (freq_thres * (zerobl / 2) / 100))) {
if (peaks[i + 1] < ((zerobl / 2) + (freq_thres * (zerobl / 2) / 100)) &&
peaks[i + 1] > ((zerobl / 2) - (freq_thres * (zerobl / 2) / 100))) {
printf("1");
zerobl = peaks[i] + 2;
i++;
}
}
else if (peaks[i] < ((zerobl / 2) + (freq_thres * (zerobl / 2) / 100)) &&
peaks[i] > ((zerobl / 2) - (freq_thres * (zerobl / 2) / 100))) {
printf("0");
#ifndef DISABLE_VC
zerobl = peaks[i];
#endif
}
}
printf("\n");
}
/* main */
int main(int argc, char *argv[])
{
int fd;
SNDFILE *sndfile = NULL;
/* configuration variables */
char *filename = NULL;
int auto_thres = AUTO_THRES, max_level = 0, use_sndfile = 0, verbose = 1;
int sample_rate = SAMPLE_RATE, silence_thres = SILENCE_THRES;
/* getopt variables */
int ch, option_index;
static struct option long_options[] = {
{"auto-thres", 0, 0, 'a'},
{"device", 1, 0, 'd'},
{"file", 1, 0, 'f'},
{"help", 0, 0, 'h'},
{"max-level", 0, 0, 'm'},
{"silent", 0, 0, 's'},
{"threshold", 0, 0, 't'},
{"version", 0, 0, 'v'},
{0, 0, 0, 0}
};
while(1) {
ch = getopt_long(argc, argv, "a:d:f:hmst:v", long_options, &option_index);
if (ch == -1)
break;
switch (ch) {
case 'a':
auto_thres = atoi(optarg);
break;
case 'd':
filename = xstrdup(optarg);
break;
case 'f':
filename = xstrdup(optarg);
use_sndfile = 1;
break;
case 'h':
print_help(stdout, argv[0]);
exit(EXIT_SUCCESS);
break;
case 'm':
max_level = 1;
break;
case 's':
verbose = 0;
break;
case 't':
auto_thres = 0;
break;
case 'v':
print_version(stdout);
exit(EXIT_SUCCESS);
break;
default:
print_help(stdout, argv[0]);
exit(EXIT_FAILURE);
break;
}
}
if (verbose) {
print_version(stderr);
fprintf(stderr, "\n");
}
if (use_sndfile && max_level) {
fprintf(stderr, "*** Error: -f and -m switches do not mix!\n");
exit(EXIT_FAILURE);
}
if (filename == NULL)
filename = xstrdup(DEVICE);
if (verbose)
fprintf(stderr, "*** Opening %s\n", filename);
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open()");
exit(EXIT_FAILURE);
}
if (use_sndfile)
sndfile = sndfile_init(fd, verbose);
else
sample_rate = dsp_init(fd, verbose);
if (max_level) {
print_max_level(fd, sample_rate);
exit(EXIT_SUCCESS);
}
if (!silence_thres) {
fprintf(stderr, "*** Error: Invalid silence threshold\n");
exit(EXIT_FAILURE);
}
if (use_sndfile)
get_sndfile(sndfile);
else
get_dsp(fd, sample_rate, silence_thres);
if (auto_thres)
silence_thres = auto_thres * evaluate_max() / 100;
if (verbose)
fprintf(stderr, "*** Silence threshold: %d (%d%% of max)\n",
silence_thres, auto_thres);
decode_aiken_biphase(FREQ_THRES, silence_thres);
close(fd);
free(sample);
exit(EXIT_SUCCESS);
return 0;
}
Permalink
Comments: 0
Leave a reply »