vBulletin Search Engine Optimization
| |||||||
| Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| Dear tech@, I have attached the patch to aucat(1) that does the following. Of course it is backward compatible. a) Creates SUN AU files b) Creates RIFF WAV files c) You can specify the duration of recording d) You can specify recording rates and few other misc options e) Now aucat(1) can also play AU and WAV files correctly I have updated the man page as well. Actually it is a rewrite. -Girish Index: aucat.1 ================================================== ================= RCS file: /store/openbsd/cvsync/src/usr.bin/aucat/aucat.1,v retrieving revision 1.15 diff -a -u -p -r1.15 aucat.1 --- aucat.1 31 May 2007 19:20:07 -0000 1.15 +++ aucat.1 9 May 2008 04:37:52 -0000 @@ -39,39 +39,72 @@ .Os .Sh NAME .Nm aucat -.Nd concatenate and play audio files +.Nd concatenate,record and play audio files .Sh SYNOPSIS .Nm aucat +.Op Fl c Ar channels .Op Fl f Ar device -.Ar +.Op Fl F Ar recordingformat[AU/WAV] +.Op Fl p Ar precision +.Op Fl r Ar rate +.Op Fl R +.Op Fl t Ar seconds +.Ar file .Sh DESCRIPTION The .Nm utility reads files sequentially, writing them to the specified device. + +When invoked with the +.Fl R +flag, +.Nm +records and writes to the specified file. Recording is supported in SUN +AU format and RIFF WAV format. + By default, .Nm plays audio through the .Pa /dev/audio -device. +device. With the +.Fl R +flag, aucat records audio samples to the file instead of playing. The .Ar file operands are processed in command line order. .Pp +In playback mode, If a Sun .au header is -detected it is skipped over and not copied to +detected it is copied to .Pa /dev/audio . If a Microsoft .wav header (RIFF) is detected it is interpreted to select the right audio encoding for playback and the data chunk of the file is copied to -.Pa /dev/audio , -given that the audio driver directly supports the encoding. +.Pa /dev/audio. Otherwise, the entire file is copied to .Pa /dev/audio . .Pp +Your sound card may not support all sample rates. The default +sample rates are set to 44,100 Hz. +.Pp The options are as follows: .Bl -tag -width "-f deviceXX" +.It Fl c Ar channels +Number of channels .It Fl f Ar device Specifies an alternate audio device. +.It Fl F Ar AU/WAV +Format of recording (AU or WAV) +.It Fl p Ar precision +Precision (8, 16, 24 or 32 bits) +.It Fl r Ar sampling rate +Rate at which recording is to be done. Typical values are 8000, 1600, +22050, 44100 or 48000 Hz. However your sound card may not support all of +them. +.It Fl R +Record instead of playback +.It Fl t Ar seconds +Stop recording after t seconds .El .Pp .Ex -std aucat Index: aucat.c ================================================== ================= RCS file: /store/openbsd/cvsync/src/usr.bin/aucat/aucat.c,v retrieving revision 1.14 diff -a -u -p -r1.14 aucat.c --- aucat.c 13 Apr 2008 22:39:29 -0000 1.14 +++ aucat.c 9 May 2008 07:50:01 -0000 @@ -37,26 +37,98 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <signal.h> +#include <sys/time.h> #include <err.h> #define _PATH_AUDIO "/dev/audio" +#define SUN_MAGIC 0x732e646e +struct wavheader { + u_int32_t riff_chunkid; + u_int32_t riff_chunksize; + u_int32_t riff_format; + u_int32_t fmt_subchunkid; + u_int32_t fmt_subchunksize; + u_int16_t fmt_format; /* 1 = pcm uncompressed */ + u_int16_t fmt_channels; /* 1 = mono, 2 = stereo */ + u_int32_t fmt_samplespersec; /* 8000, 22050, 44100 etc. */ + u_int32_t fmt_byterate; /* total bytes per second */ + u_int16_t fmt_blockalign; /* channels * bitspersample/8 */ + u_int16_t fmt_bitspersample; + u_int32_t data_header; /* 'data' */ + u_int32_t data_len; /* length of data */ +} wavheader; + +/* + * AU header format + */ +struct auheader { + u_int32_t magic; + u_int32_t hdr_size; + u_int32_t data_size; + u_int32_t encoding; + u_int32_t sample_rate; + u_int32_t channels; +} auheader; + + /* - * aucat: concatenate and play Sun 8-bit .au files or 8/16-bit - * uncompressed WAVE RIFF files + * aucat: concatenate,record and play Sun 8-bit .au files + * or 8/16-bit uncompressed WAVE RIFF files */ +void write_word(u_int32_t *, u_int32_t); +void write_header(int); int playfile(int, audio_info_t *); +int playwavefile(int, audio_info_t *); +int playaufile(int, audio_info_t *); int readwaveheader(int, audio_info_t *); +int readauheader(int, audio_info_t *); + +/* Globals */ +int channels, encoding, rate, seconds, precision; +char format[512]; +u_int32_t file_len; +audio_info_t ai; +int filefd, afd; __dead void usage(void); -int afd; +void +write_word(u_int32_t *var, u_int32_t val) +{ + struct word { + u_int16_t first; + u_int16_t second; + } word; + + word.first = word.second = 0; + word.first = (val >> 16) & 0x0000ffff; + word.second = val & 0x0000ffff; + memcpy(var, &word, sizeof(struct word)); + + return; +} + +void +write_header(int sig) +{ + if (strncasecmp(format, "WAV", 3) == 0) { + writewaveheader(); + } + else if (strncasecmp(format, "AU", 2) == 0) { + writeauheader(); + } + + return; +} /* - * function playfile: given a file which is positioned at the beginning - * of what is assumed to be an .au data stream copy it out to the audio + * Given a file which is positioned at the beginning + * of what is assumed to be raw data stream copy it out to the audio * device. Return 0 on success, -1 on failure. */ + int playfile(int fd, audio_info_t *audioinfo) { @@ -85,6 +157,69 @@ playfile(int fd, audio_info_t *audioinfo } /* + * playwavefile: given a file which is positioned at the beginning + * of what is assumed to be an .wav data stream copy it out to the audio + * device. Return 0 on success, -1 on failure. + */ +int +playwavefile(int fd, audio_info_t *audioinfo) +{ + ssize_t rd; + u_int16_t sample; + + /* + * If we don't wait here, the AUDIO_SETINFO ioctl interrupts + * the playback of the previous file. + */ + if (ioctl(afd, AUDIO_DRAIN, NULL) == -1) + warn("AUDIO_DRAIN"); + + if (ioctl(afd, AUDIO_SETINFO, audioinfo) == -1) { + warn("AUDIO_SETINFO"); + return -1; + } + + while ((rd = read(fd, &sample, sizeof(sample))) > 0) { + sample = letoh16(sample); + if (write(afd, &sample, rd) != rd) + warn("write"); + } + + return (0); +} +/* + * function playaufile: given a file which is positioned at the beginning + * of what is assumed to be an .au data stream copy it out to the audio + * device. Return 0 on success, -1 on failure. + */ +int +playaufile(int fd, audio_info_t *audioinfo) +{ + ssize_t rd; + u_int16_t sample; + + /* + * If we don't wait here, the AUDIO_SETINFO ioctl interrupts + * the playback of the previous file. + */ + if (ioctl(afd, AUDIO_DRAIN, NULL) == -1) + warn("AUDIO_DRAIN"); + + if (ioctl(afd, AUDIO_SETINFO, audioinfo) == -1) { + warn("AUDIO_SETINFO"); + return -1; + } + + while ((rd = read(fd, &sample, sizeof(sample))) > 0) { + sample = betoh16(sample); + if (write(afd, &sample, rd) != rd) + warn("write"); + } + + return (0); +} + +/* * function readwaveheader: given a file which is positioned at four * bytes into a RIFF file header, read the rest of the header, check * to see if it is a simple WAV file that we can handle, seek to the @@ -94,39 +229,23 @@ playfile(int fd, audio_info_t *audioinfo int readwaveheader(int fd, audio_info_t *audioinfo) { - /* - * The simplest form of a RIFF file... - */ - struct { - /* u_int32_t riff_chunkid; -- this is read before in main()! */ - u_int32_t riff_chunksize; - u_int32_t riff_format; - - u_int32_t fmt_subchunkid; - u_int32_t fmt_subchunksize; - - u_int16_t fmt_format; /* 1 = PCM uncompressed */ - u_int16_t fmt_channels; /* 1 = mono, 2 = stereo */ - u_int32_t fmt_samplespersec; /* 8000, 22050, 44100 etc. */ - u_int32_t fmt_byterate; /* total bytes per second */ - u_int16_t fmt_blockalign; /* channels * bitspersample/8 */ - u_int16_t fmt_bitspersample; /* 8 = 8 bits, 16 = 16 bits etc. */ - } header; u_int datatag; + u_int32_t data; char c; /* * Is it an uncompressed wave file? */ - if (read(fd, &header, sizeof(header)) != sizeof(header)) { + if (read(fd, &wavheader, sizeof(wavheader)) + != sizeof(wavheader)) { warn("read"); return -1; } - if (strncmp((char *) &header.riff_format, "WAVE", 4) || - letoh16(header.fmt_format) != 1 || - strncmp((char *) &header.fmt_subchunkid, "fmt ", 4) || - (letoh16(header.fmt_bitspersample) != 8 && - letoh16(header.fmt_bitspersample) != 16)) + if (strncmp((char *) &wavheader.riff_format, "WAVE", 4) || + letoh16(wavheader.fmt_format) != 1 || + strncmp((char *) &wavheader.fmt_subchunkid, "fmt ", 4) || + (letoh16(wavheader.fmt_bitspersample) != 8 && + letoh16(wavheader.fmt_bitspersample) != 16)) return -1; /* @@ -165,41 +284,231 @@ readwaveheader(int fd, audio_info_t *aud return -1; } + lseek(fd, sizeof(struct wavheader), SEEK_SET); + + audioinfo->play.sample_rate = + letoh32(wavheader.fmt_samplespersec); + audioinfo->play.channels = + letoh16(wavheader.fmt_channels); + audioinfo->play.precision = + letoh16(wavheader.fmt_bitspersample); + audioinfo->play.encoding = audioinfo->play.precision == 8 ? + AUDIO_ENCODING_ULINEAR : AUDIO_ENCODING_SLINEAR_LE; + return 0; +} + +int +readauheader(int fd, audio_info_t *audioinfo) +{ + u_int32_t sample, val; + + if (read(fd, &auheader, sizeof(auheader)) + != sizeof(auheader)) { + warn("read"); + return -1; + } + /* - * Ignore the size of the data chunk. + * Seek past the AU header */ - if (lseek(fd, 4, SEEK_CUR) == -1) { + if (lseek(fd, betoh32(auheader.hdr_size), SEEK_SET) == -1) { warn("lseek"); return -1; } - audioinfo->play.sample_rate = letoh32(header.fmt_samplespersec); - audioinfo->play.channels = letoh16(header.fmt_channels); - audioinfo->play.precision = letoh16(header.fmt_bitspersample); - audioinfo->play.encoding = audioinfo->play.precision == 8 ? - AUDIO_ENCODING_ULINEAR : AUDIO_ENCODING_SLINEAR_LE; + audioinfo->play.sample_rate = betoh32(auheader.sample_rate); + audioinfo->play.channels = betoh32(auheader.channels); + audioinfo->play.encoding = betoh32(auheader.encoding); return 0; } +/* Record to AU format */ +int +recau(int fd) +{ + ssize_t rd; + u_int16_t sample; + + file_len = 0; + + + filefd = fd; + + ai.record.sample_rate = rate; + ai.record.channels = channels; + ai.record.precision = precision; + ai.record.encoding = AUDIO_ENCODING_LINEAR; + + if (ioctl(afd, AUDIO_SETINFO, &ai) == -1) { + warn("AUDIO_SETINFO"); + return -1; + } + + + lseek(fd, sizeof(struct auheader), SEEK_SET); + while ((rd = read(afd, &sample, 2)) > 0) { + file_len +=rd; + sample = htobe16(sample); + if (write(fd, (char *)&sample, rd) != rd) { + warn("write"); + } + } + + if (rd == -1) { + warn("read"); + } + + return file_len; +} + +/* Record to WAV format */ +int +recwave(int fd) +{ + ssize_t rd; + u_int16_t sample; + + file_len = 0; + + filefd = fd; + + ai.record.sample_rate = rate; + ai.record.channels = channels; + ai.record.precision = precision; + ai.record.encoding = AUDIO_ENCODING_LINEAR; + + if (ioctl(afd, AUDIO_SETINFO, &ai) == -1) { + warn("AUDIO_SETINFO"); + return -1; + } + + lseek(fd, sizeof(struct wavheader), SEEK_SET); + while ((rd = read(afd, &sample, 2)) > 0) { + file_len +=rd; + sample = htole16(sample); + if (write(fd, (char *)&sample, rd) != rd) { + warn("write"); + } + } + + if (rd == -1) { + warn("read"); + } + + return file_len; +} + +/* little endian format */ +int +writewaveheader() +{ + u_int32_t datalen; /* length of recording */ + + /* 'RIFF' */ + write_word(&wavheader.riff_chunkid, 0x49524646); + datalen = file_len - sizeof(struct wavheader); + wavheader.riff_chunksize = htole32(datalen); + /* 'WAVE' */ + write_word(&wavheader.riff_format, 0x41574556); + /* 'fmt\0' */ + write_word(&wavheader.fmt_subchunkid, 0x6d662074); + wavheader.fmt_subchunksize =htole32(16); + wavheader.fmt_format = htole16(1); + wavheader.fmt_channels = htole16(channels); + wavheader.fmt_samplespersec = htole32(rate); + wavheader.fmt_byterate = htole32( rate * precision / 8 ); + wavheader.fmt_blockalign = htole16(channels * precision / 8); + wavheader.fmt_bitspersample = htole16(precision); + /* 'data' */ + write_word(&wavheader.data_header, 0x61646174); + wavheader.data_len = htole32(datalen); + + /* + * write an uncompressed wave file header + */ + lseek(filefd, 0, SEEK_SET); + if (write(filefd, &wavheader, sizeof(wavheader)) + != sizeof(wavheader)) { + warn("read"); + return -1; + } + + return 0; +} + + +/* Big endian format */ +int +writeauheader() +{ + /* ".snd" */ + write_word(&auheader.magic, SUN_MAGIC); + auheader.hdr_size = htobe32(24); + auheader.data_size = htobe32(file_len - 24); + auheader.encoding = htobe32(3); + auheader.sample_rate = htobe32(rate); + auheader.channels = htobe32( 1); + + /* + * write AU header + */ + lseek(filefd, 0, SEEK_SET); + if (write(filefd, &auheader, sizeof(auheader)) + != sizeof(auheader)) { + warn("read"); + return -1; + } + + return 0; +} + + int main(int argc, char *argv[]) { - int fd, ch; + int fd, ch,playflag; u_int32_t data; char magic[4]; char *dev; - audio_info_t ai; audio_info_t ai_defaults; + struct itimerval it; + sigset_t sig; + struct sigaction sa; dev = getenv("AUDIODEVICE"); if (dev == NULL) dev = _PATH_AUDIO; - while ((ch = getopt(argc, argv, "f:")) != -1) { + /* defaults */ + channels = 1; + rate = 44100; + precision = 16; + playflag = 1; + seconds = 0; + + while ((ch = getopt(argc, argv, "c:f:F:r switch (ch) { + case 'c': + channels = strtonum(optarg, 1, 16, NULL); + break; case 'f': dev = optarg; break; + case 'F': + strncpy(format, optarg, sizeof(format)); + break; + case 'r': + rate = strtonum(optarg, 8000, 48000, NULL); + break; + case 'p': + precision = strtonum(optarg, 8, 32, NULL); + break; + case 'R': + playflag = 0; + break; + case 't': + seconds = strtonum(optarg, 1, 32768, NULL); + break; default: usage(); /* NOTREACHED */ @@ -210,50 +519,87 @@ main(int argc, char *argv[]) if (argc == 0) usage(); + if (playflag == 1) { + if ((afd = open(dev, O_WRONLY)) < 0) + err(1, "can't open %s", dev); + } else { + if ((afd = open(dev, O_RDONLY)) < 0) + err(1, "can't open %s", dev); + } - if ((afd = open(dev, O_WRONLY)) < 0) - err(1, "can't open %s", dev); if (ioctl(afd, AUDIO_GETINFO, &ai_defaults) == -1) err(1, "AUDIO_GETINFO"); + AUDIO_INITINFO(&ai); while (argc) { - if ((fd = open(*argv, O_RDONLY)) < 0) - err(1, "cannot open %s", *argv); - - AUDIO_INITINFO(&ai); - - ai.play.sample_rate = ai_defaults.play.sample_rate; - ai.play.channels = ai_defaults.play.channels; - ai.play.encoding = ai_defaults.play.encoding; - ai.play.precision = ai_defaults.play.precision; - - if (read(fd, magic, sizeof(magic)) != sizeof(magic) || - strncmp(magic, ".snd", 4)) { - /* - * not an .au file, bad header. - * Check if it could be a .wav file and set - * the playback parameters in ai. - */ - if (strncmp(magic, "RIFF", 4) || - readwaveheader(fd, &ai)) { - /* - * Assume raw audio data since that's - * what /dev/audio generates by default. - */ - if (lseek(fd, 0, SEEK_SET) == -1) - warn("lseek"); + if (playflag == 1) { + if ((fd = open(*argv, O_RDONLY)) < 0) + err(1, "cannot open %s for reading", *argv); + + ai.play.sample_rate = ai_defaults.play.sample_rate; + ai.play.channels = ai_defaults.play.channels; + ai.play.encoding = ai_defaults.play.encoding; + ai.play.precision = ai_defaults.play.precision; + + /* Read the header */ + read(fd, magic, sizeof(magic)); + lseek(fd, 0, SEEK_SET); + + if (strncmp(magic, "RIFF", 4) == 0) { + readwaveheader(fd, &ai); + if (playwavefile(fd, &ai) < 0) + exit(1); + } else if (strncmp(magic, ".snd", 4) == 0) { + readauheader(fd, &ai); + if (playaufile(fd, &ai) < 0) + exit(1); + } else { + /* It is a raw file */ + if (playfile(fd, &ai) < 0) + exit(1); } } else { - if (read(fd, &data, sizeof(data)) == sizeof(data)) { - data = ntohl(data); - if (lseek(fd, (off_t)data, SEEK_SET) == -1) - warn("lseek"); + if ((fd = open(*argv, O_CREAT|O_WRONLY)) < 0) + err(1, "cannot open %s for writing", + *argv); + + ai.record.sample_rate = + ai_defaults.record.sample_rate; + ai.record.channels = + ai_defaults.record.channels; + ai.record.encoding = + ai_defaults.record.encoding; + ai.record.precision = + ai_defaults.record.precision; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = seconds; + it.it_value.tv_usec = 0; + + sigemptyset(&sig); + sigaddset(&sig, SIGALRM); + sigaddset(&sig, SIGINT); + bzero(&sa, sizeof sa); + sigemptyset(&sa.sa_mask); + sa.sa_handler = write_header; + sigaction(SIGALRM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + setitimer(ITIMER_REAL, &it, NULL); + + if (strncasecmp(format, "WAV", 3) == 0) { + if (recwave(fd) < 0) + exit(1); + } + else if (strncasecmp(format, "AU", 2) == 0) { + if (recau(fd) < 0) + exit(1); } + + } - if (playfile(fd, &ai) < 0) - exit(1); (void) close(fd); argc--; argv++; @@ -266,6 +612,10 @@ usage(void) { extern char *__progname; - fprintf(stderr, "usage: %s [-f device] file ...\n", __progname); + fprintf(stderr, "\n%s \n [-c channels]\n [-f device]\n" + " [-F recordingformat(AU/WAV)]\n" + " [-r rate]\n [-R] \n [-p precision]\n" + " [-t seconds]\n" + " file ...\n", __progname); exit(1); } |