123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- /*
- * main.c
- *
- * Copyright (C) 2023 bzt
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * @brief Main file for "cassette" audio-binary converter
- *
- */
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- uint8_t *decode(uint8_t *data, size_t size, int fmt, int en, int nb, int sy, int fm, int ng, int length, int hdr, int skip, size_t *outlen);
- uint8_t *encode(uint8_t *data, size_t size, int fmt, int en, int nb, int sy, int fm, int ng, int length, int hdr, int rate, int ol, int zl, int bitrate, int quality, size_t *outlen);
- int verbose = 0;
- /**
- * Usage instructions
- */
- void usage(char *cmd)
- {
- printf("Cassette by bzt GPLv3+\r\nhttps://gitlab.com/bztsrc/cassette\r\n\r\n");
- printf("%s [opts|-s] <in.wav|in.mp3> <out.bin>\r\n", cmd);
- printf("%s [opts] <in.bin> <out.wav>\r\n", cmd);
- printf("%s [opts|-b|-q] <in.bin> <out.mp3>\r\n\r\n", cmd);
- printf(" -v | -vv be verbose\r\n");
- printf("Data encoding options\r\n");
- printf(" -e endianess, least significant bit comes first\r\n");
- printf(" -7 assume 7 bits per byte on the tape\r\n");
- printf(" -0 no sync bit (default, 0 = 0, 1 = 1)\r\n");
- printf(" -1 two bits, first is sync bit (10 = 0, 11 = 1)\r\n");
- printf(" -2 two bits, second is sync bit (01 = 0, 11 = 1)\r\n");
- printf(" -3 three bits, two sync bits (101 = 0, 111 = 1)\r\n");
- printf(" -d FM, frequency modulated, down-edge (defaults to AM)\r\n");
- printf(" -u FM, frequency modulated, up-edge\r\n");
- printf(" -du FM, frequency modulated, both edges\r\n");
- printf("Sample encoding options\r\n");
- printf(" -h do not detect but generate Homelab header\r\n");
- printf(" -s input is signed 8-bit PCM\r\n");
- printf(" -S <n> skip N samples at the beginning\r\n");
- printf(" -l <usec> one bit's length in microsec (eg. 770)\r\n");
- printf(" -g <usec> gap between bytes (AM) or diff in zero and one lengths (FM)\r\n");
- printf(" -r <rate> sample rate (eg. 44100 or 48000)\r\n");
- printf(" -o <num> bit set level (eg. 127)\r\n");
- printf(" -z <num> bit clear level (eg. -32)\r\n");
- printf("MP3 encoding options\r\n");
- printf(" -b <bitrate> bit rate (eg. 96, 128 or 192)\r\n");
- printf(" -q <quality> quality (0 best, 9 worst)\r\n\r\n");
- printf("You should have received a copy of the GNU General Public License\r\n");
- printf("along with this program. If not, see <http://www.gnu.org/licenses/>.\r\n\r\n");
- exit(1);
- }
- /**
- * Main function
- */
- int main(int argc, char **argv)
- {
- FILE *f;
- int i, j;
- int length = 0, en = 0, nb = 8, sy = 0, fm = 0, ng = 0, hdr = 0, skip = 0, rate = 44100, ol = 127, zl = -65536;
- int bitrate = 96, quality = 0, fmt = 0;
- char *infile = NULL, *outfile = NULL, *ext;
- uint8_t *data = NULL, *out = NULL;
- size_t size;
- /* parse command line */
- for(i = 1; i < argc; i++)
- if(argv[i][0] == '-')
- switch(argv[i][1]) {
- case 'l': length = atoi(argv[++i]); break;
- case 'g': ng = atoi(argv[++i]); break;
- case 'r': rate = atoi(argv[++i]); break;
- case 'o': ol = atoi(argv[++i]); break;
- case 'z': zl = atoi(argv[++i]); break;
- case 'b': bitrate = atoi(argv[++i]); break;
- case 'q': quality = atoi(argv[++i]); break;
- case 'S': skip = atoi(argv[++i]); break;
- default:
- for(j = 1; argv[i][j]; j++)
- switch(argv[i][j]) {
- case 's': fmt = 1; break;
- case 'd': fm |= 1; break;
- case 'u': fm |= 2; break;
- case 'e': en = 1; break;
- case '7': nb = 7; break;
- case '0': sy = 0; break;
- case '1': sy = 1; break;
- case '2': sy = 2; break;
- case '3': sy = 3; break;
- case 'h': hdr = 1; break;
- case 'v': verbose++; break;
- default: usage(argv[0]); break;
- }
- break;
- }
- else
- if(!infile) infile = argv[i]; else
- if(!outfile) outfile = argv[i]; else usage(argv[0]);
- if(!infile || !outfile) usage(argv[0]);
- if(zl == -65536) zl = fm ? -127 : -64;
- /* read input file */
- f = fopen(infile, "rb");
- if(f) {
- fseek(f, 0, SEEK_END);
- size = (size_t)ftell(f);
- fseek(f, 0, SEEK_SET);
- data = (uint8_t*)malloc(size);
- if(!data) { fprintf(stderr, "cassette: unable to allocate memory\r\n"); exit(2); }
- (void)fread(data, 1, size, f);
- fclose(f);
- } else {
- fprintf(stderr, "cassette: unable to open '%s'\r\n", infile);
- exit(1);
- }
- /* do the thing */
- ext = strrchr(outfile, '.');
- if(ext && (!strcmp(ext, ".wav") || !strcmp(ext, ".mp3")))
- out = encode(data, size, ext && !strcmp(ext, ".mp3"), en, nb, sy, fm, ng, length, hdr, rate, ol, zl, bitrate, quality, &size);
- else
- out = decode(data, size, fmt, en, nb, sy, fm, ng, length, !hdr, skip, &size);
- /* write output file */
- if(size > 0 && out) {
- f = fopen(outfile, "wb");
- if(f) {
- (void)fwrite(out, 1, size, f);
- fclose(f);
- } else {
- fprintf(stderr, "cassette: unable to write '%s'\r\n", outfile);
- }
- }
- /* free resources */
- if(out) free(out);
- if(data) free(data);
- return 0;
- }
|