////////////////// // // Program to create simple sounds (such as sine waves) // and write them to a .wav audio file. // For details, see usage message below. // // might be better to use: // http://www.mega-nerd.com/libsndfile/ // // Of course, most of what is implemented here can be done // easily using standard command-line tools, e.g. 'sox', // for example: // :; play -n synth 1 sine 10000-12000 fade h 0.1 1 0.1 using namespace std; /* needed by some versions of the compiler */ #include #include /* for bcopy */ #include #include #include #include #include #include #include #include #include /* for perror */ #include "arg_parser.h" typedef unsigned short int uint16; typedef unsigned int uint32; // reference level is saturation minus 15 dB const double ref_level(SHRT_MAX * pow(10, -15./20.)); #define PCMfmt 1 #define stereo 2 #define xxnchan stereo #define xxtime 10 #define xxfreq 550 #define xxrate 44100 #define xxival 1.2 #define xxname "sine.wav" class WAV_header { public: char riff[4]; /* the characters "RIFF" */ uint32 file_length; /* file length - 8 */ char wave[8]; /* the characters "WAVEfmt " */ uint32 offset; /* position of dataheader-20 (usually 16) */ uint16 format; /* 1 = PCM */ uint16 nchans; /* #channels (eg. 2=stereo) */ uint32 framesec; /* #frames/sec (eg. 44100 for CD rate) */ uint32 bytesec; /* #bytes/sec (see note 1) */ uint16 byteframe; /* #bytes/frame (see note 1) */ uint16 bitsamp; /* #bits/sample (see note 1) */ // char otherstuff[N]; /* N = offset-16 (see note 5) */ char dataheader[4]; /* the characters "data" */ uint32 datalen; /* #bytes of actual data */ char databuf[0]; // REFERENCE: // http://homepages.nildram.co.uk/~abcomp/pcaudiofaq.htm inline WAV_header(){ bcopy("RIFF", riff, sizeof(riff)); bcopy("WAVEfmt ", wave, sizeof(wave)); bcopy("data", dataheader, sizeof(dataheader)); format = PCMfmt; offset = ((char*)(&dataheader)) - ((char*)(&format)); } void inline setup( const uint32 _framesec, const uint16 _nchans, const uint16 _bitsamp, const double _ttime ){ framesec = _framesec; nchans = _nchans; bitsamp = _bitsamp; byteframe = nchans*bitsamp/8; bytesec = byteframe * framesec; datalen = uint32(_ttime * bytesec); file_length = datalen + sizeof(WAV_header) - 8; } }; int bits = 16; double pi = M_PI; typedef short int datum; int verbosity(0); string progname; void usage(const int err) { (err ? cerr : cout) << "Create a simple sound (such as a sine wave) and write it to a .wav file\n" "Usage: " << progname << " [options]\n\n" "Example: " << progname << " -fre 8000 -t 1 -o - | aplay -\n" "\n" "Options:\n" "-amplitude [] Amplitude [= 0] dB relative to -15dbFS\n" "-channels [] number of channels [=" << xxnchan << "] (1=mono, 2=stereo)\n" "-frequency [] left-channel frequency [= " << xxfreq << "] Hz;\n" "-interval [] each successive channel freq is this much [= " << xxival << "] higher.\n" "-endfrequency [] left-channel frequency at end of sweep [same as at start]\n" "-time [] total time [= " << xxtime << "] seconds\n" "-output-file [] output filename [= " << xxname << "]; '-' means STDOUT\n" "-help print this message\n" "-verbosity print more progress reports and debugging messages\n" "-FrameRate [] frame rate [= " << xxrate << "] Hz (note capitalization)\n" "\n" "Options can be abbrevated as much as you dare.\n" "\n" "The sweep from freq to endfreq is exponential in frequency space,\n" "i.e. linear in musical note space (octaves per second).\n" "\n" "Output file format:\n" " sine.wav: RIFF (little-endian) data, WAVE audio,\n" " Microsoft PCM, 16 bit, stereo 44100 Hz\n" " 1764044 bytes (10 seconds)\n" ;;;; exit(err); } int Atoi(const string foo){ return atoi(foo.c_str()); } int Atof(const string foo){ return atof(foo.c_str()); } int main(int argc, char** argv) { progname = argv[0]; unsigned int where = progname.rfind('/'); // find final slash if (where != progname.npos) { progname = progname.substr(1+where); } WAV_header hdr; double calamp(0.); // default amplitude (dB relative to -15dBFS) double calfreq(xxfreq); double endfreq(xxfreq); int saw_endfreq(0); int frate(xxrate); double ttime(xxtime); // total time / seconds int nchan(xxnchan); // e.g. 2 means stereo double shift=0; string ofn(xxname); double ival(xxival); arg_parser arg(argc, argv); string progname = arg.next(); try {for (;;){ if (!arg.argc) break; arg.next(); if (0) {} else if (arg.prefix("-amplitude", 1)) { calamp = Atof(arg.next()); } else if (arg.prefix("-frequency", 1)) { calfreq = Atof(arg.next()); } else if (arg.prefix("-endfrequency", 1)) { endfreq = Atof(arg.next()); saw_endfreq++; } else if (arg.prefix("-time", 1)) { ttime = Atof(arg.next()); } else if (arg.prefix("-channels", 1)) { nchan = Atoi(arg.next()); } else if (arg.prefix("-interval", 1)) { ival = Atof(arg.next()); } else if (arg.prefix("-FrameRate", 1)) { frate = Atoi(arg.next()); } else if (arg.prefix("-ofile", 1)) { ofn = arg.next(); } else if (arg.prefix("-verbosity")) { verbosity++; } else if (arg.prefix("-help")) { usage(0); exit(0); } else if (arg.prefix("-")) { cerr << "Option '" << arg.cur << "' not recognized\n"; exit(1); } else { cerr << "Extraneous verbiage: '" << arg.cur << "'\n"; exit(1); } }} catch (string err){ cerr << err << endl; exit(1); } if (!saw_endfreq) endfreq = calfreq; if (calfreq <= 0 || endfreq <= 0){ cerr << "Frequency and endfrequency must both be positive." << endl; exit(1); } double noct = log(endfreq/calfreq) / log(2.); if (verbosity) { cerr << "Span: " << noct << " octaves" << endl; cerr << "Time: " << ttime << " seconds" << endl; cerr << "Rate: " << noct/ttime << " octaves per second" << endl; } double f0 = calfreq; uint32 nn = uint32(frate*ttime); // total number of frames double gfactor(pow(10, calamp/20)); double amplitude(ref_level * gfactor); hdr.setup(frate, nchan, bits, ttime); datum wave[nn][nchan]; int ounit(1); // default to STDOUT if (ofn != "-") { ounit = open (ofn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 00644); if (ounit < 0) { cerr << "Error opening output file: " << ofn << endl; exit(1); } } // standard constructor for double initializes phases to zero: vector phase(nchan); double sweep(1); if (nn > 1) sweep = pow(endfreq/calfreq, 1.0/(nn-1)); for (uint32 ii = 0; ii < nn; ii++) { double fch = f0; for (int ch = 0; ch < nchan; ch++){ wave[ii][ch] = datum(shift + amplitude*sin(phase[ch])); phase[ch] += 2*pi*fch/frate; fch *= ival; } f0 *= sweep; } int todo; int rslt; todo = sizeof(hdr); rslt = write(ounit, &hdr, todo); if (rslt != todo) { cerr << "Short write " << endl; perror(0); exit(1); } todo = sizeof(wave); rslt = write(ounit, wave, todo); if (rslt != todo) { cerr << "Short write" << endl; perror(0); exit(2); } close(ounit); }