#include #include #include #include #include #define unsigned32 unsigned long #define signed32 long #define unsigned16 unsigned int #define PKSCALE 32767 #define PPDITHER 2.0 void write_WAVE_header(FILE *f, /* Output file */ unsigned16 nchannels, /* Number of audio channels */ unsigned32 nsamples, /* Number of audio samples */ unsigned32 samplerate) /* Sample rate */ { /* See http://www.sonicspot.com/guide/wavefiles.html for .wav format. Fixed at 16-bit uncompressed PCM samples with no unnecessary chunks. */ unsigned32 chunkdatasize, avebytespersec; unsigned16 samplefield, sigbitspersample, compressioncode, blockalign; compressioncode = 1; /* Uncompressed PCM samples */ sigbitspersample = 16; /* 16 bit samples (must be 8n) */ samplefield = 2; /* Stored in a 2-byte field */ fprintf(f,"RIFF"); /* RIFF chunk header [4 bytes] */ chunkdatasize = 36 + nchannels * samplefield * nsamples; /* RIFF chunk data size */ fputc((chunkdatasize & 0x000000ff),f); /* [4 bytes] */ fputc((chunkdatasize & 0x0000ff00) >> 8,f); fputc((chunkdatasize & 0x00ff0000) >> 16,f); fputc((chunkdatasize & 0xff000000) >> 24,f); fprintf(f,"WAVE"); /* Start of RIFF chunk data [4 bytes] */ fprintf(f,"fmt "); /* Start of (sub-) chunk: "fmt " [4 bytes] */ chunkdatasize = 16; /* Chunk data size [16 bytes]. No extra format */ /* bytes are needed for uncompressed PCM */ fputc((chunkdatasize & 0x000000ff),f); /* [4 bytes] */ fputc((chunkdatasize & 0x0000ff00) >> 8,f); fputc((chunkdatasize & 0x00ff0000) >> 16,f); fputc((chunkdatasize & 0xff000000) >> 24,f); fputc((compressioncode & 0x00ff),f); /* Compression code for samples */ fputc((compressioncode & 0xff00) >> 8,f); fputc((nchannels & 0x00ff),f); /* Number of channels */ fputc((nchannels & 0xff00) >> 8,f); fputc((samplerate & 0x000000ff),f); /* Sample rate (samples/second) */ fputc((samplerate & 0x0000ff00) >> 8,f); fputc((samplerate & 0x00ff0000) >> 16,f); fputc((samplerate & 0xff000000) >> 24,f); avebytespersec = samplerate * sigbitspersample/8 * nchannels; /* Average data rate, bytes/s */ fputc((avebytespersec & 0x000000ff),f); fputc((avebytespersec & 0x0000ff00) >> 8,f); fputc((avebytespersec & 0x00ff0000) >> 16,f); fputc((avebytespersec & 0xff000000) >> 24,f); blockalign = sigbitspersample/8 * nchannels; /* Block alignment */ fputc((blockalign & 0x00ff),f); fputc((blockalign & 0xff00) >> 8,f); fputc((sigbitspersample & 0x00ff),f); /* Significant bits per sample */ fputc((sigbitspersample & 0xff00) >> 8,f); fprintf(f,"data"); /* Start of next (sub-) chunk: "data" [4 bytes] */ chunkdatasize = nchannels * samplefield * nsamples; /* Chunk data size */ fputc((chunkdatasize & 0x000000ff),f); /* [4 bytes] */ fputc((chunkdatasize & 0x0000ff00) >> 8,f); fputc((chunkdatasize & 0x00ff0000) >> 16,f); fputc((chunkdatasize & 0xff000000) >> 24,f); /* Digital audio samples follow this header ... */ } double RPDF_noise() { /* 1.0 V pk-pk */ return(drand48() - 0.5); } double TPDF_noise() { /* 1.0 V pk-pk -- two RPDF signals added together gives TPDF */ return(0.5 * (RPDF_noise() + RPDF_noise())); } double silence() { return(0.0); } double tone_sweep_lin_pulse(double t, double fstart, double frate) { /* 1.0 V pk-pk raised-cosine pulsed tones */ double t0, dt, p1, p2, w0, a0; t0 = floor(t); /* 1-second period number */ dt = t - t0; /* Offset into current 1-second period */ p1 = 0.25; /* Start at +250 ms */ p2 = 0.75; /* Stop at +750 ms */ w0 = 0.0125; /* 12.5 ms raised-cosine transition */ if (dt <= (p1 - w0 / 2)) { a0 = 0.0; } else if (dt <= (p1 + w0 / 2)) { a0 = 0.25 + 0.25 * sin ( M_PI * (dt - p1) / w0 ); } else if (dt <= (p2 - w0/2)) { a0 = 0.5; } else if (dt <= (p2 + w0 / 2)) { a0 = 0.25 - 0.25 * sin ( M_PI * (dt - p2) / w0 ); } else { a0 = 0.0; } return(a0 * sin(2 * M_PI * (fstart + t0 * frate) * t)); } double tone_sweep_log(double t, double tmax, double fstart, double fend) { /* 1.0 V pk-pk */ double k; k = (1 / tmax) * log(fend / fstart); return(0.5 * sin(2 * M_PI * fstart / k * exp(k * t))); } double tone_sweep_lin(double t, double tmax, double fstart, double fend) { return(0.5 * sin(2 * M_PI * t * (fstart + t/tmax/2*(fend-fstart)))); } double tone(double t, double f) { /* 1.0 V pk-pk */ return(0.5 * sin(2 * M_PI * f * t)); } void write_signal(FILE *out, /* Output file */ unsigned16 nchannels, /* Number of audio channels */ unsigned32 nsamples, /* Number of audio samples */ unsigned32 samplerate) /* Sample rate */ { unsigned32 i,j; /* Sample counters */ double t; /* Time */ double lsignal, rsignal; signed32 lsample, rsample; for (i = 0; i < nsamples; i++) { /* Time now */ t = i * (1.0/samplerate); /* Generate the signal starting from zero */ lsignal = 0.0; rsignal = 0.0; /* Sweep starting from 0 Hz at 1,000 Hz/s at -1 dBFS peak */ /* lsignal += pow(10.0, -1.0/20.0) * tone_sweep_lin(t, nsamples * (1.0/samplerate), 0.0, 0.0 + 1000.0 * nsamples * (1.0/samplerate)); */ /* Aliasing: 23,000 Hz at -4 dBFS RMS */ /* lsignal += pow(10.0, -4.0/20.0) * sqrt(2.0) * tone(t,23000.0); */ /* THD: 1,000 Hz at -4 dBFS RMS */ lsignal += pow(10.0, -4.0/20.0) * sqrt(2.0) * tone(t,1000.0); /* IMD: 60 Hz at -5 dBFS RMS plus 7,000 Hz at -17 dBFS RMS */ /* lsignal += pow(10.0, -5.0/20.0) * sqrt(2.0) * tone(t, 60.0); lsignal += pow(10.0,-17.0/20.0) * sqrt(2.0) * tone(t,7000.0); */ /* DYN (dynamic range): 1,000 Hz at -4 dBFS RMS plus 8,000 Hz */ /* at -93.3 dBFS RMS */ /* lsignal += pow(10.0, -4.0/20.0) * sqrt(2.0) * tone(t, 250.0); lsignal += pow(10.0,-93.3/20.0) * sqrt(2.0) * tone(t,8000.0); */ rsignal = lsignal; /* Convert s.t. 1.0 V pk-pk becomes 0dBFS in 16-bit signed samples */ /* including TPDF dither */ lsample = lround(lsignal*(2*PKSCALE+1) + TPDF_noise()*PPDITHER); rsample = lround(rsignal*(2*PKSCALE+1) + TPDF_noise()*PPDITHER); /* Final clip at 16 bits */ if (lsample > PKSCALE) lsample = PKSCALE; if (lsample < -PKSCALE) lsample = -PKSCALE-1; if (rsample > PKSCALE) rsample = PKSCALE; if (rsample < -PKSCALE) rsample = -PKSCALE-1; /* printf("%7ld %7ld\n",lsample,rsample); */ /* Write to file as 16 bit mono or stereo; zero other channels */ for (j = 0; j < nchannels; j++) { if (j == 0) { fputc((lsample & 0x00ff),out); fputc((lsample & 0xff00) >> 8,out); } else if (j == 1) { fputc((rsample & 0x00ff),out); fputc((rsample & 0xff00) >> 8,out); } else { fputc((0 & 0x00ff),out); fputc((0 & 0xff00) >> 8,out); } } } } int main(int argc, char *argv[]) { FILE *outfile; /* Output file pointer */ FILE *randfile; /* Kernel random file pointer */ unsigned32 sr; /* Sample rate (samples/second) */ unsigned32 ns; /* Number of samples */ unsigned16 nc; /* Number of channels */ double duration; /* Duration (seconds) */ /* Seed the RNG */ randfile = fopen("/dev/urandom","r"); srand((unsigned short)fgetc(randfile)<<8 + (unsigned short)fgetc(randfile)); fclose(randfile); /* Basic setup */ sr = 48000; /* Sample rate */ nc = 2; /* Channels */ duration = (131072*2.5+1)/44100.0; /* Duration */ /* Open the output data file */ outfile = fopen("test.wav","w"); /* How many samples to generate? */ ns = ceil(duration * sr); /* Write the header */ write_WAVE_header(outfile, nc, ns, sr); /* Write the signal */ write_signal(outfile, nc, ns, sr); /* Close the files */ fclose(outfile); /* Exit */ return(0); }