/*********************************************/ /* stereosoften.c 0.2.1 (2000-Mar-14-Tue) */ /* by Adam M. Costello **************************/ /* */ /* stereosoften [ -d ] [ -g ] [ -v ] */ /* */ /* Filters a stereo audio signal by adding a delayed attenuated copy */ /* of each channel to the opposite channel. This is appropriate for */ /* "hard" stereo recordings, where some instruments or vocals are */ /* entirely on one channel, which can be very uncomfortable to listen */ /* to using headphones. With appropriate delay and gain parameters, */ /* those instruments/vocals will still sound like they are coming */ /* completely from one side, but the discomfort will be eliminated. */ /* */ /* In more detail, this filter reads from stdin stereo pairs */ /* of linear 16-bit signed native-format integer samples, and */ /* outputs a similar stream where left_out(t) = left_in(t) * x + */ /* right_in(t-) * y (and vice-versa swapping left and right), */ /* and x = sqrt(1 / (1 + *)), and y = * x, so that */ /* x*x + y*y = 1, so that the total power of the two channels is */ /* independent of the parameter. */ /* */ /* The is the amplitude of the delayed signal relative to the */ /* undelayed signal, and defaults to 0.9. */ /* */ /* The is in samples, and defaults to 20 (which for the CD */ /* sample rate is the amount of time it takes sound to travel about 6 */ /* inches through air). */ /* */ /* The first time overflow occurs, the program prints a message to */ /* stderr, and sets the exit status to unsuccessful, but does not */ /* terminate early. */ /* */ /* The final output is multiplied by , which defaults to */ /* 1/sqrt(2) (about 0.707), which guarantees that overflow will not */ /* occur regardless of . If is greater than 1/(x+y), */ /* then overflow can occur, but often will not. For near 1.0, */ /* 1/(x+y) is a little more than 1/sqrt(2). */ /**********************************************************************/ /* This is ANSI C code. */ #include #include #include #include /* int16 must be a 2-byte signed integer (we'll check later): */ typedef short int16; #define INT16_MAX SHRT_MAX #define INT16_MIN SHRT_MIN static void usage(const char *progname) { fprintf(stderr, "usage\n%s [ -d ] [ -a ]\n", progname); exit(EXIT_FAILURE); } static void fail(const char *msg) { fprintf(stderr,msg); exit(EXIT_FAILURE); } int main(int argc, char **argv) { enum { batchsize = 1024 }; int16 (*inbatch)[2], (*outbatch)[2]; int delay = 20, i, c, r, w, overflow; float gain = 0.9, volume = 1/sqrt(2), x, y, xx, yy, out; const float toohigh = (float) INT16_MAX + 1.0, toolow = (float) INT16_MIN - 1.0; char **argp; if (sizeof (int16) != 2) fail("Sorry, our integer type is not 2 bytes.\n"); for (argp = argv + 1; *argp; ++argp) { if (!strcmp(*argp, "-d")) { if (!*++argp) usage(argv[0]); delay = atoi(*argp); if (delay < 0) fail(" must be nonnegative\n"); } else if (!strcmp(*argp, "-g")) { if (!*++argp) usage(argv[0]); gain = atof(*argp); } else if (!strcmp(*argp, "-v")) { if (!*++argp) usage(argv[0]); volume = atof(*argp); } else usage(argv[0]); } inbatch = calloc(batchsize + delay, 4); outbatch = calloc(batchsize, 4); if (!inbatch || !outbatch) fail("out of memory\n"); x = sqrt(1.0 / (1.0 + gain * gain)); y = gain * x; xx = x * volume; yy = y * volume; overflow = 0; for (;;) { r = fread(inbatch + delay, 4, batchsize, stdin); if (r <= 0) break; for (i = 0; i <= r; ++i) { for (c = 0; c <= 1; ++c) { out = inbatch[i + delay][c] * xx + inbatch[i][!c] * yy; out += out >= 0 ? 0.5 : -0.5; if (!overflow && (out >= toohigh || out <= toolow)) { overflow = 1; fprintf(stderr, "overflow has occurred\n"); } outbatch[i][c] = out; } } w = fwrite(outbatch,4,r,stdout); if (w != r) break; memmove(inbatch, inbatch + r, delay * 4); } if (ferror(stdin)) { perror(argv[0]); exit(EXIT_FAILURE); } if (ferror(stdout)) { perror(argv[0]); exit(EXIT_FAILURE); } return overflow ? EXIT_FAILURE : EXIT_SUCCESS; }