From 67e4ee3d5f42927bca5bf0996e9b2de3c6df60dd Mon Sep 17 00:00:00 2001 From: ep00ch Date: Thu, 6 Oct 2022 00:07:18 -0700 Subject: [PATCH] Added new c2mc for converting MC-10 .c10 files to audio. --- Makefile | 5 +- README.md | 7 + bin/c2mc_arm | Bin 0 -> 51621 bytes c2mc.c | 619 +++++++++++++++++++++++++++++++++++++++++++++++++++ c2mc.h | 11 + 5 files changed, 641 insertions(+), 1 deletion(-) create mode 100755 bin/c2mc_arm create mode 100644 c2mc.c create mode 100644 c2mc.h diff --git a/Makefile b/Makefile index c2a3166..491d037 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ macos: bin/c2t_x86 bin/c2t_arm bin/c2t-96h_x86 bin/c2t-96h_arm dist: macos windows clean: testclean - rm -f c2t.h bin/c2t bin/c2t-96h bin/c2t.exe bin/c2t-96h.exe bin/c2t_x86 bin/c2t_arm bin/c2t-96h_x86 bin/c2t-96h_arm + rm -f c2t.h bin/c2t bin/c2t-96h bin/c2t.exe bin/c2t-96h.exe bin/c2t_x86 bin/c2t_arm bin/c2t-96h_x86 bin/c2t-96h_arm bin/c2mc_arm cd asm; make clean # nix @@ -38,6 +38,9 @@ bin/c2t_arm: c2t.c c2t.h bin/c2t-96h_arm: c2t-96h.c c2t.h $(CC) -Wall -Wno-strict-aliasing -Wno-unused-value -Wno-unused-function -I. -O3 -target arm64-apple-macos11 -o $@ c2t-96h.c -lm +bin/%_arm: %.c %.h + $(CC) -Wall -Wno-strict-aliasing -Wno-unused-value -Wno-unused-function -I. -O3 -target arm64-apple-macos11 -o $@ $< -lm + # windows bin/c2t.exe: c2t.c c2t.h $(WIN32GCC) -Wall -Wno-strict-aliasing -Wno-unused-value -Wno-unused-function -I. -O3 -o bin/c2t.exe c2t.c diff --git a/README.md b/README.md index 585e278..989fa9f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +## This Fork +`c2mc` is a command line tool that can convert .c10 binary code/data into audio files suitable for use with the Radio Shack TRS-80 Model MC-10 cassette interface. No frills yet. I have only tested with macOS arm64: +``` +make clean +make bin/c2mc_arm +``` + ## Introduction `c2t` is a command line tool that can convert binary code/data and/or Apple-1/II Monitor text, as well as 140K disk images, into audio files suitable for use with the Apple-1 and II (II, II+, //e) cassette interface. diff --git a/bin/c2mc_arm b/bin/c2mc_arm new file mode 100755 index 0000000000000000000000000000000000000000..ab73997d68a16e6e659fa9faee93ee2d04654038 GIT binary patch literal 51621 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 literal 0 HcmV?d00001 diff --git a/c2mc.c b/c2mc.c new file mode 100644 index 0000000..e76c50d --- /dev/null +++ b/c2mc.c @@ -0,0 +1,619 @@ +/* + +c2mc, .c10 to TRS-80 MC-10 audio, Version 0.1, Wed Oct 05 + +Parts copyright (c) 2014 All Rights Reserved, Egan Ford (egan@sense.net) + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +PARTICULAR PURPOSE. + +Built on work by: + * Paul Bourke (http://paulbourke.net/dataformats/audio/, AIFF and WAVE output code) + * Malcolm Slaney and Ken Turkowski (Integer to IEEE 80-bit float code) + +License: + * Do what you like, remember to credit all sources when using. + +Description: + This small utility will read TRS-80 MC-10 .c10 binaries and output TRS-80 MC-10 AIFF + and WAV audio files for use with a cassette interface. + +Features: + * Big and little-endian machine support. + o Little-endian tested. + * AIFF and WAVE output (both tested). + * Platforms tested: + o 32-bit/64-bit x86 OS/X. + o 32-bit/64-bit x86 Linux. + o 32-bit x86 Windows/Cygwin. + o 32-bit x86 Windows/MinGW. + +Notes: + * Dropbox only supports .wav and .aiff (do not use .wave or .aif) + +Not yet done: + * Test big-endian. + * gnuindent + * Redo malloc code in appendtone + +Thinking about: + * Check for existing file and abort, or warn, or prompt. + * -q quiet option for Makefiles + +Bugs: + * Probably + +*/ + +#include +#include +#include +#include +#include +#include +#include "c2mc.h" + +#define ABS(x) (((x) < 0) ? -(x) : (x)) + +#define VERSION "Version 0.1" +#define OUTFILE argv[argc-1] +#define BINARY 0 +#define MONITOR 1 +#define AIFF 2 +#define WAVE 3 +#define DSK 4 + +#define WRITERBYTE(x) { \ + unsigned char wb_j, wb_temp=(x); \ + for(wb_j=0;wb_j<8;wb_j++) { \ + if(wb_temp & 1) \ + appendtone(&output,&outputlength,freq1,rate,0,1,&offset); \ + else \ + appendtone(&output,&outputlength,freq0,rate,0,1,&offset); \ + wb_temp>>=1; \ + } \ +} + +void usage(); +char *getext(char *filename); +void appendtone(double **sound, long *length, int freq, int rate, double time, double cycles, int *offset); +void Write_AIFF(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); +void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); +void ConvertToIeeeExtended(double num, unsigned char *bytes); + +int square = 1; +//int square = 0; + +typedef struct seg { + int start; + int length; + int codelength; + unsigned char *data; + char filename[256]; +} segment; + +int main(int argc, char **argv) +{ + FILE *ofp; + double *output = NULL, amp=0.75; + long outputlength=0; + int i, j, c, outputtype, offset=0, fileoutput=1; + int /*longmon=0,*/ rate=9600, bits=8, freq0=1200, freq1=2400; + char *filetypes[] = {"binary","monitor","aiff","wave","disk"}; + char *ext; + //unsigned char pop, parity; + unsigned int numseg = 0; + segment *segments = NULL; + + opterr = 1; + while((c = getopt(argc, argv, "vph?r:")) != -1) + switch(c) { + case 'v': // version + fprintf(stderr,"\n%s\n\n",VERSION); + return 1; + break; + case 'p': // stdout + fileoutput = 0; + break; + case 'h': // help + case '?': + usage(); + return 1; + case 'r': // override rate for -1/-2 only + rate = atoi(optarg); + break; + } + + if(argc - optind < 1 + fileoutput) { + usage(); + return 1; + } + + // read intput files + + fprintf(stderr,"\n"); + for(i=optind;i 0 && j >= 0x93 && segments[i].data[j] == 0x55) { + /* "A blank section of tape approximately equal to 0.5 seconds + in length; this allows BASIC time to evaluate the Namefile." + This must mean 5 milliseconds, MC-10 is not THAT slow.*/ + appendtone(&output,&outputlength,0,rate,nameblank,0,&offset); + nameblank = 0; + } + + WRITERBYTE(segments[i].data[j]); + } + } + + // append zero to zero out last wave + appendtone(&output,&outputlength,0,rate,0,1,&offset); + + // 0.1 sec quiet to help some emulators + //appendtone(&output,&outputlength,0,rate,0.1,0,&offset); + + // 0.4 sec quiet to help some IIs + // appendtone(&output,&outputlength,0,rate,0.4,0,&offset); + if (nameblank > 0) { + fclose(ofp); + fprintf(stderr,"ERROR: Unable to find Namefile block."); + exit(65); //EX_DATAERR + } + // write it + if(outputtype == AIFF) + Write_AIFF(ofp,output,outputlength,rate,bits,amp); + else if(outputtype == WAVE) + Write_WAVE(ofp,output,outputlength,rate,bits,amp); + + fclose(ofp); + return 0; +} + +void appendtone(double **sound, long *length, int freq, int rate, double time, double cycles, int *offset) +{ + long i, n=time*rate; + static long grow = 0; + double *tmp = NULL; + + if(freq && cycles) + n=cycles*rate/freq; + + if(n == 0) + n=cycles; + +/* + if((tmp = (double *)realloc(*sound, (*length + n) * sizeof(double))) == NULL) + abort(); + *sound = tmp; +*/ + +// new code for speed up Windows realloc + if(*length + n > grow) { + grow = *length + n + 10000000; + if((tmp = (double *)realloc(*sound, (grow) * sizeof(double))) == NULL) + abort(); + *sound = tmp; + } + +//tmp -> (*sound) + if(square) { + int j; + + if(freq) + for (i = 0; i < n; i++) { + for(j = 0;j < rate / freq / 2;j++) + (*sound)[*length + i++] = 1; + for(j = 0;j < rate / freq / 2;j++) + (*sound)[*length + i++] = -1; + i--; + } + else + for (i = 0; i < n; i++) + (*sound)[*length + i] = 0; + } + else + for(i=0;i=0;i--) { + if(filename[i] == '.') + break; + stack[sp++] = filename[i]; + } + stack[sp] = '\0'; + + if(sp == strlen(filename) || sp == 0) + return(NULL); + + if((rval = (char *)malloc(sp * sizeof(char))) == NULL) + ; //do error code + + rval[sp] = '\0'; + for(i=0;i> 24, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x000000ff), fptr); + fprintf(fptr, "AIFF"); + + // Write the common chunk + fprintf(fptr, "COMM"); + fputc(0, fptr); // Size + fputc(0, fptr); + fputc(0, fptr); + fputc(18, fptr); + fputc(0, fptr); // Channels = 1 + fputc(1, fptr); + fputc((nsamples & 0xff000000) >> 24, fptr); // Samples + fputc((nsamples & 0x00ff0000) >> 16, fptr); + fputc((nsamples & 0x0000ff00) >> 8, fptr); + fputc((nsamples & 0x000000ff), fptr); + fputc(0, fptr); // Size = 16 + fputc(bits, fptr); + + ConvertToIeeeExtended(nfreq, bit80); + for (i = 0; i < 10; i++) + fputc(bit80[i], fptr); + + // Write the sound data chunk + fprintf(fptr, "SSND"); + fputc((((bits / 8) * nsamples + 8) & 0xff000000) >> 24, fptr); // Size + fputc((((bits / 8) * nsamples + 8) & 0x00ff0000) >> 16, fptr); + fputc((((bits / 8) * nsamples + 8) & 0x0000ff00) >> 8, fptr); + fputc((((bits / 8) * nsamples + 8) & 0x000000ff), fptr); + fputc(0, fptr); // Offset + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); // Block + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + + // Find the range + themin = samples[0]; + themax = themin; + for (i = 1; i < nsamples; i++) { + if (samples[i] > themax) + themax = samples[i]; + if (samples[i] < themin) + themin = samples[i]; + } + if (themin >= themax) { + themin -= 1; + themax += 1; + } + themid = (themin + themax) / 2; + themin -= themid; + themax -= themid; + if (ABS(themin) > ABS(themax)) + themax = ABS(themin); +// scale = amp * 32760 / (themax); + scale = amp * ((bits == 16) ? 32760 : 124) / (themax); + + // Write the data + for (i = 0; i < nsamples; i++) { + if (bits == 16) { + v = (unsigned short) (scale * (samples[i] - themid)); + fputc((v & 0xff00) >> 8, fptr); + fputc((v & 0x00ff), fptr); + } else { + v = (unsigned char) (scale * (samples[i] - themid)); + fputc(v, fptr); + } + } +} + +/* + Write an WAVE sound file + Only do one channel, only support 16 bit. + Supports any (reasonable) sample frequency + Little/big endian independent! +*/ + +// egan: changed code to support 8 bit. + +void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp) +{ + unsigned short v; + int i; + unsigned long totalsize, bytespersec; + double themin, themax, scale, themid; + + // Write the form chunk + fprintf(fptr, "RIFF"); + totalsize = (bits / 8) * nsamples + 36; + fputc((totalsize & 0x000000ff), fptr); // File size + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0xff000000) >> 24, fptr); + fprintf(fptr, "WAVE"); + fprintf(fptr, "fmt "); // fmt_ chunk + fputc(16, fptr); // Chunk size + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + fputc(1, fptr); // Format tag - uncompressed + fputc(0, fptr); + fputc(1, fptr); // Channels + fputc(0, fptr); + fputc((nfreq & 0x000000ff), fptr); // Sample frequency (Hz) + fputc((nfreq & 0x0000ff00) >> 8, fptr); + fputc((nfreq & 0x00ff0000) >> 16, fptr); + fputc((nfreq & 0xff000000) >> 24, fptr); + bytespersec = (bits / 8) * nfreq; + fputc((bytespersec & 0x000000ff), fptr); // Average bytes per second + fputc((bytespersec & 0x0000ff00) >> 8, fptr); + fputc((bytespersec & 0x00ff0000) >> 16, fptr); + fputc((bytespersec & 0xff000000) >> 24, fptr); + fputc((bits / 8), fptr); // Block alignment + fputc(0, fptr); + fputc(bits, fptr); // Bits per sample + fputc(0, fptr); + fprintf(fptr, "data"); + totalsize = (bits / 8) * nsamples; + fputc((totalsize & 0x000000ff), fptr); // Data size + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0xff000000) >> 24, fptr); + + // Find the range + themin = samples[0]; + themax = themin; + for (i = 1; i < nsamples; i++) { + if (samples[i] > themax) + themax = samples[i]; + if (samples[i] < themin) + themin = samples[i]; + } + if (themin >= themax) { + themin -= 1; + themax += 1; + } + themid = (themin + themax) / 2; + themin -= themid; + themax -= themid; + if (ABS(themin) > ABS(themax)) + themax = ABS(themin); +// scale = amp * 32760 / (themax); + scale = amp * ((bits == 16) ? 32760 : 124) / (themax); + + // Write the data + for (i = 0; i < nsamples; i++) { + if (bits == 16) { + v = (unsigned short) (scale * (samples[i] - themid)); + fputc((v & 0x00ff), fptr); + fputc((v & 0xff00) >> 8, fptr); + } else { + v = (unsigned char) (scale * (samples[i] - themid)); + fputc(v + 0x80, fptr); + } + } +} + + +/* + * C O N V E R T T O I E E E E X T E N D E D + */ + +/* Copyright (C) 1988-1991 Apple Computer, Inc. + * All rights reserved. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +#ifndef HUGE_VAL +#define HUGE_VAL HUGE +#endif /*HUGE_VAL */ + +#define FloatToUnsigned(f) ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1) + +void ConvertToIeeeExtended(double num, unsigned char *bytes) +{ + int sign; + int expon; + double fMant, fsMant; + unsigned long hiMant, loMant; + + if (num < 0) { + sign = 0x8000; + num *= -1; + } else { + sign = 0; + } + + if (num == 0) { + expon = 0; + hiMant = 0; + loMant = 0; + } else { + fMant = frexp(num, &expon); + if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ + expon = sign | 0x7FFF; + hiMant = 0; + loMant = 0; /* infinity */ + } else { /* Finite */ + expon += 16382; + if (expon < 0) { /* denormalized */ + fMant = ldexp(fMant, expon); + expon = 0; + } + expon |= sign; + fMant = ldexp(fMant, 32); + fsMant = floor(fMant); + hiMant = FloatToUnsigned(fsMant); + fMant = ldexp(fMant - fsMant, 32); + fsMant = floor(fMant); + loMant = FloatToUnsigned(fsMant); + } + } + + bytes[0] = expon >> 8; + bytes[1] = expon; + bytes[2] = hiMant >> 24; + bytes[3] = hiMant >> 16; + bytes[4] = hiMant >> 8; + bytes[5] = hiMant; + bytes[6] = loMant >> 24; + bytes[7] = loMant >> 16; + bytes[8] = loMant >> 8; + bytes[9] = loMant; +} + diff --git a/c2mc.h b/c2mc.h new file mode 100644 index 0000000..217984d --- /dev/null +++ b/c2mc.h @@ -0,0 +1,11 @@ + +const char *usagetext="\n\ +usage: c2mc [-vh?]\n\ + c2mc [-r #] inputfile.c10 ... [outputfile.[aif[f]|wav[e]]]\n\ +\n\ + -h|? this help\n\ + -r #, where # overrides the sample rate (e.g. -r 48000)\n\ + -v print version number and exit\n\ +\n\ +"; +