bplay-0.991/0040755000411700001440000000000007161510300012023 5ustar davidmusersbplay-0.991/COPYING0100644000411700001440000004311007012115311013050 0ustar davidmusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. bplay-0.991/Makefile0100644000411700001440000000041207012115311013453 0ustar davidmusersPROG = bplay OBJS = bplay.o sndfunc.o shmbuf.o SRCS = bplay.c sndfunc.o shmbuf.c CC = gcc CFLAGS = -Wall -O3 -DUSEBUFFLOCK #-DDEBUG LDFLAGS = LIBS = $(PROG): $(OBJS) $(CC) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) ln -sf bplay brec clean: rm -f bplay brec *.o *~ bplay-0.991/README0100644000411700001440000000704007161510216012707 0ustar davidmusersThis is version 0.991 of my buffered audio play/record program for Linux. Note that this is BETA software and may not work for you. Please mail me with any bugs/feature enhancements etc. This version is purely a maintenance release; all new work is going into bplay2 which I would like to release soonish, but real life keeps getting in the way. If you receive a BINARY version of this software and it doesn't work, I advise you to get the source and rebuild it. Certain interfaces to the kernel sound driver have evolved over the life of the Linux kernel and a program compiled for one version may not work correctly with another kernel version. Recompiling fixes this problem. Please note that this code originally dates from 1994! Features: Handles the following file types: Raw sound files (no header) Micro$oft WAV files Creative Labs Voc files (including the 16bit stereo variants and with support for loops). There are a couple of limitations with the VOC recording; in particular only the v1.20 format files are produced, which won't work on really old player programs. In addition there is an upper limit of about 16M on the size of a VOC file. Details The program uses two processes and a large block of shared memory to allow asynchronous reading and writing of the file and the device. It also runs at the highest possible priority. Using this method it is possible to record at full speed (44.1kHz 16 bit stereo, or about 176Kb/s) without overruns on 486DX/33 using 8bit DMA with a slow IDE disk in multiuser mode until the disk gets very close to full (ie the head is seeking all over the disk to find free blocks and can't keep up). Building Basically, type make. Note that there are two commented options on the CFLAGS command line Adding -DDEBUG just causes lots of debugging messages to be printed to the screen. Adding -DUSEBUFFLOCK compiles in some code that causes the shared memory to be locked down in memory. Although this seems to be a good idea, on older Linux versions it caused lockups (I think a deadlock in the kernel) and I haven't tested it for about a year, so use at own risk. Installing bplay/brec should be installed suid root. This is so that the program can renice itself to a higer priority. As far as I am aware there are no security holes in this program (it only uses the root privs when it has to and then immediately drops them again). Check the code and see what you think if you are worried. Make a link (symbolic or hard, doesn't matter) from brec to bplay. Install the man pages somewhere sensible. You will have to edit the brec manpage if you don't install it in ...../man1/brec.1 CHANGES 0.991 Added patch from Peter Makholm to fix endian problems with wav files. Added patch from Nick Andrew to allow Ctrl-C to do the right thing when recording. CREDITS Thanks to Huba Gaspar for finding a locking bug. Thanks to various debian people for debianizing it and makeing various improvements - I think they are Wichert Akkerman , Peter Makholm , Ian Jackson , and Norbert Veber . TODO Tidy up and generalize the code. Improve the Makefile and add an installation target. Improve the documentation. Add support for the realtime scheduling extensions in the new Linux kernels. Fix the recording limits on WAV and VOC files. Add support for other formats (IRCAM floating point etc). Merge in the SunOS/Ariel version. Provide a Tk front end. David Monro davidm@amberdata.demon.co.uk davidm@cs.usyd.edu.au bplay-0.991/bplay.10100644000411700001440000000512207047004326013222 0ustar davidmusers.TH BPLAY 1 "20 September 1999" .SH NAME bplay, brec \- buffered sound recording/playing .SH SYNOPSIS .B bplay [\-d device] [\-B buffersize] [\-S] [\-s speed] [\-b bits] [[\-t secs] | [\-T samples]] [[\-j secs] | [\-J samples]] [\-D level] [file] .sp .B brec [\-d device] [\-B buffersize] [\-S] [\-s speed] [\-b bits] [[\-t secs] | [\-T samples]] [\-r|\-v|\-w] [\-D level] [file] .SH DESCRIPTION .B bplay copies data from the named sound file (or the standard input if no filename is given) to the audio device. .PP .B brec copies data from the audio device to the named sound file (or the standard output if no filename is present). .PP These programs are intended to be drop\-in replacements for the .B vplay and .B vrec programs by Michael Beck (beck@informatik.hu-berlin.de). .SS OPTIONS .TP .I "\-B buffersize" Use the supplied audio buffer size instead of the default. .TP .I "\-d device" Use the supplied audio device instead of the default. .TP .I "\-S" Sound file is stereo. .TP .I "\-s speed" The speed in samples per second. .TP .I "\-b bits" The number of bits per sample. Only 8 and 16 are currently supported. .TP .I "\-t secs" The number of seconds to be played or recorded. .TP .I "\-T samples" The number of samples to be played or recorded. .TP .I "\-j secs" When playing, the number of seconds to skip at the beginning of the input before playing. .TP .I "\-J samples" When playing, the number of samples to skip at the beginning of the input before playing. .TP .I "\-r" When recording, write raw sound file. .TP .I "\-v" When recording, write Creative Labs VOC sound file. .TP .I "\-w" When recording write Microsoft Wave sound file. .TP .I "\-D level" Print debug information to stderr. Debug level ranges from 0 to 2, where 0 is no debug information. .SH FILES .I /dev/dsp The audio device. .SH BUGS .PP The \-t, \-T, \-j and \-J options may do strange things when playing VOC files. .PP There are limitations on recording VOC format files - specifically VOC files are only recorded in the 1.20 version of the format, which some player programs may choke on. There is also currently a limit of around 16M on the size of a VOC file which will be recorded. This is probably not a problem since I don't think anybody really uses VOC files anymore. .PP This program prefers to run setuid root. This is because it wants to use setpriority() to run at the highest possible priority, and also locks down the buffers it uses to avoid them being swapped out. .SH AUTHOR David Monro (davidm@amberdata.demon.co.uk or davidm@cs.usyd.edu.au) .PP The option parsing code was originally taken from .B vplay to maintain compatability. bplay-0.991/bplay.c0100644000411700001440000003365207161507661013324 0ustar davidmusers/* ** brec/bplay.c (C) David Monro 1996 ** ** Copyright under the GPL - see the file COPYING in this directory ** ** */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fmtheaders.h" /* Types and constants */ typedef enum sndf_t {F_UNKNOWN, F_WAV, F_VOC, F_RAW} sndf_t; #define MSPEED 1 #define MBITS 2 #define MSTEREO 4 /* Globals */ char *progname; int forked; pid_t pid; int recorder = 0; int debug = 0; /* Prototypes */ /* From my . I wonder why it doesn't work there?!? */ extern char *basename __P ((__const char *__filename)); void Usage(void); void ErrDie(char *err); void Die(char *err); void getbcount(int speed, int bits, int stereo, int *bcount, int timelim, int samplim, int timejmp, int sampjmp, int *bjump); void playraw(int thefd, char hd_buf[20], int speed, int bits, int stereo); void playwav(int thefd, char hd_buf[20], int mods, int speed, int bits, int stereo); void playvoc(int thefd, char hd_buf[20]); /* extern globals */ extern int audio, abuf_size, fmt_mask; extern char *audev; extern int bigbuffsize; /* extern prototypes */ extern void init_sound(int recorder); extern void snd_parm(int speed, int bits, int stereo); extern void init_shm(void); extern void shmrec(int outfd, int bcount, int terminate); extern void diskread(int outfd, int bcount, char hd_buf[20], int terminate, int speed, int bits, int stereo); extern volatile void audiowrite(void); extern void initsems(int disks, int snds); extern void cleanupsems(void); int main(int argc, char *argv[]) { int thefd; /* The file descriptor */ int speed, bits, stereo; /* Audio parameters */ int timelim; /* Recording time in secs */ int samplim; /* Recording time in samples */ int timejmp; /* Skip time in secs */ int sampjmp; /* Skip time in samples */ int bcount; /* Number of bytes to record */ int bjump; /* Number of bytes to skip */ int themask; /* Permission mask for file */ sndf_t filetype; /* The file type */ int mods; /* So user can override */ int optc; /* For getopt */ progname = basename(argv[0]); /* For errors */ /* Ok, find out if we record or play */ if (strcmp(progname, "brec") == 0) recorder = 1; else { if (!(strcmp(progname, "bplay") == 0)) Die("must be called as bplay or brec"); else recorder = 0; } /* Default values */ speed = 8000; bits = 8; stereo = 0; timelim = 0; samplim = 0; bcount = 0; timejmp = 0; sampjmp = 0; bjump = 0; filetype = F_UNKNOWN; mods = 0; /* Parse the options */ while ((optc = getopt(argc, argv, "Ss:b:t:T:j:J:rvwd:B:D:")) != -1) { switch(optc) { case 's': speed = atoi(optarg); if (speed < 300) speed *= 1000; mods |= MSPEED; break; case 'b': bits = atoi(optarg); if ((bits != 8) && (bits != 16)) Usage(); mods |= MBITS; break; case 'S': stereo = 1; mods |= MSTEREO; break; case 't': timelim = atoi(optarg); break; case 'T': samplim = atoi(optarg); break; case 'j': timejmp = atoi(optarg); break; case 'J': sampjmp = atoi(optarg); break; case 'r': filetype = F_RAW; break; case 'v': filetype = F_VOC; break; case 'w': filetype = F_WAV; break; case 'd': audev = optarg; break; case 'B': bigbuffsize = atoi(optarg); break; case 'D': debug = atoi(optarg); break; default: Usage(); } } #if 1 /* This program is either set-uid or run by root (I hope...) */ if (!getuid()) { if (setpriority(PRIO_PROCESS, 0, -20) == -1) fprintf(stderr, "%s: setpriority: %s: continuing anyway\n", progname, strerror(errno)); } #endif /* Drop out of suid mode before we open any files */ if(setreuid(geteuid(), getuid()) == -1) { fprintf(stderr, "%s: setreuid: %s: continuing anyway\n", progname, strerror(errno)); fprintf(stderr, "real uid = %d, effective uid = %d\n", getuid(), geteuid()); } /* Calculate the time limit in samples if need be */ if (optind > argc - 1) { if(recorder) /* No file, so stdin or stdout */ thefd = 1; else thefd = 0; } else { /* Open a file */ if(recorder) { /* Ok, get the mask for the opening the file */ themask = umask(0); umask(themask); if ((thefd = open(argv[optind], O_CREAT | O_TRUNC | O_WRONLY, (~themask) & 0666)) == -1) ErrDie(argv[optind]); } else if ((thefd = open(argv[optind], O_RDONLY)) == -1) ErrDie(argv[optind]); } /* Open and set up the audio device */ init_sound(recorder); /* Check if the card is capable of the requested operation */ /* ** Can't check for stereo yet, just number of bits. Also, ** can't check for 12 bit operations yet. */ #if 0 if ((bits == 8) & !(fmt_mask & AFMT_U8)) Die("Format not supported by audio device"); if ((bits == 16) & !(fmt_mask & AFMT_S16_BE)) Die("Format not supported by audio device"); #endif /* Set up the shared buffers and semaphore blocks */ init_shm(); /* MUST be called after init_sound() */ /* Call the appropriate routine */ if (recorder) { if ((timelim == 0) && (samplim == 0)) { bcount = INT_MAX; } else { getbcount(speed, bits, stereo, &bcount, timelim, samplim, timejmp, sampjmp, &bjump); } if (debug) fprintf(stderr, "bcount: %d\n", bcount); if (filetype == F_UNKNOWN) filetype = F_RAW; /* Change to change default */ switch(filetype) { case F_WAV: /* Spit out header here... */ fprintf(stderr, "Writing MS WAV sound file"); { wavhead header; char *riff = "RIFF"; char *wave = "WAVE"; char *fmt = "fmt "; char *data = "data"; memcpy(&(header.main_chunk), riff, 4); header.length = sizeof(wavhead) - 8 + bcount; memcpy(&(header.chunk_type), wave, 4); memcpy(&(header.sub_chunk), fmt, 4); header.sc_len = 16; header.format = 1; header.modus = stereo + 1; header.sample_fq = speed; header.byte_p_sec = ((bits > 8)? 2:1)*(stereo+1)*speed; header.byte_p_spl = ((bits > 8)? 2:1)*(stereo+1); header.bit_p_spl = bits; memcpy(&(header.data_chunk), data, 4); header.data_length = bcount; write(thefd, &header, sizeof(header)); } case F_RAW: if (filetype == F_RAW) fprintf(stderr, "Writing raw sound file"); fprintf(stderr, ", %dHz, %dbit, %s\n", speed, bits, (stereo)? "stereo":""); snd_parm(speed, bits, stereo); initsems(0, 1); shmrec(thefd, bcount, 1); break; case F_VOC: /* Spit out header here... */ fprintf(stderr, "Writing CL VOC sound file"); fprintf(stderr, ", %dHz, %dbit, %s\n", speed, bits, (stereo)? "stereo":""); { vochead header; blockTC ablk; blockT9 bblk; int i; char fill = 0; for (i=0;i<20;i++) header.Magic[i] = VOC_MAGIC[i]; header.BlockOffset = 0x1a; header.Version = 0x0114; header.IDCode = 0x111F; write(thefd, &header, sizeof(vochead)); snd_parm(speed, bits, stereo); initsems(0, 1); i = (bcount >= 0xFFFFF2)? 0xFFFFF2 + 12 : bcount; ablk.BlockID = 9; ablk.BlockLen[0] = (i + 12) & 0xFF; ablk.BlockLen[1] = ((i + 12) >> 8) & 0xFF; ablk.BlockLen[2] = ((i + 12) >> 16) & 0xFF; bblk.SamplesPerSec = speed; bblk.BitsPerSample = bits; bblk.Channels = stereo + 1; bblk.Format = (bits == 8)? 0 : 4; write(thefd, &ablk, sizeof(ablk)); write(thefd, &bblk, sizeof(bblk)); shmrec(thefd, i, 1); write(thefd, &fill, 1); } break; default: Die("internal error - fell out of switch"); } } else { int count; char hd_buf[20]; /* Holds first 20 bytes */ count = read(thefd, hd_buf, 20); if (count < 0) ErrDie("read"); if (count < 20) Die("input file less than 20 bytes long."); initsems(1, 0); pid = fork(); if(!pid) audiowrite(); /* Doesn't return */ forked = 1; /* Pick the write output routine */ if(strstr(hd_buf, VOC_MAGIC) != NULL) playvoc(thefd, hd_buf); else if(strstr(hd_buf, "RIFF") != NULL) playwav(thefd, hd_buf, mods, speed, bits, stereo); else /* Assume raw data */ playraw(thefd, hd_buf, speed, bits, stereo); wait(NULL); cleanupsems(); } return -1; /* We should never reach this, but lets keep gcc happy */ } void Usage(void) { fprintf(stderr, "Usage: %s [-d device] [-B buffersize] [-S] [-s Hz] [-b 8|16] [-t secs] [-D level] [-r|-v|-w] [filename]\n", progname); exit(1); } void ErrDie(char * err) { fprintf(stderr, "%s: %s: %s\n", progname, err, strerror(errno)); exit(-1); } void Die(char * err) { fprintf(stderr, "%s: %s\n", progname, err); if (forked) { kill(pid,9); } exit(-1); } void getbcount(int speed, int bits, int stereo, int *bcount, int timelim, int samplim, int timejmp, int sampjmp, int *bjump) { if(timelim) { *bcount = speed*timelim*(bits/8); if (stereo) *bcount <<= 1; } if(samplim) { *bcount = samplim*(bits/8); if (stereo) *bcount <<= 1; } if(timejmp) { *bjump = speed*timejmp*(bits/8); if (stereo) *bjump <<= 1; } if(sampjmp) { *bjump = sampjmp*(bits/8); if (stereo) *bjump <<= 1; } } void playraw(int thefd, char hd_buf[20], int speed, int bits, int stereo) { fprintf(stderr, "Playing raw data : %d bit, Speed %d %s ...\n", bits, speed, (stereo)? "Stereo" : "Mono"); diskread(thefd, 0, hd_buf, 1, speed, bits, stereo); } void playwav(int thefd, char hd_buf[20], int mods, int speed, int bits, int stereo) { wavhead wavhd; int count; memcpy((void*)&wavhd, (void*)hd_buf, 20); count = read(thefd, ((char*)&wavhd)+20, sizeof(wavhd) - 20); #if __BYTE_ORDER == __BIG_ENDIAN #include /* let's do a bit of reordering */ wavhd.length = bswap_32 (wavhd.length); wavhd.sc_len = bswap_32 (wavhd.sc_len); wavhd.format = bswap_16 (wavhd.format); wavhd.modus = bswap_16 (wavhd.modus); wavhd.sample_fq = bswap_32 (wavhd.sample_fq); wavhd.byte_p_sec = bswap_32 (wavhd.byte_p_sec); wavhd.byte_p_spl = bswap_16 (wavhd.byte_p_spl); wavhd.bit_p_spl = bswap_16 (wavhd.bit_p_spl); wavhd.data_chunk = bswap_32 (wavhd.data_chunk); wavhd.data_length = bswap_32 (wavhd.data_length); #endif if(wavhd.format != 1) Die("input is not a PCM WAV file"); if (! (mods&MSPEED)) speed = wavhd.sample_fq; if (! (mods&MBITS)) bits = wavhd.bit_p_spl; if (! (mods&MSTEREO)) stereo = wavhd.modus - 1; fprintf(stderr, "Playing WAVE : %d bit, Speed %d %s ...\n", bits, speed, (stereo)? "Stereo" : "Mono"); diskread(thefd, 0, NULL, 1, speed, bits, stereo); } void playvoc(int thefd, char hd_buf[20]) { int count; int speed=0, bits=0, stereo=0; int inloop=0, loop_times; long bytecount, loop_pos=0; vochead vochd; blockTC ccblock; int lastblocktype = -1; int quit = 0; fprintf(stderr, "Playing Creative Labs Voice file ...\n"); memcpy((void*)&vochd, (void*)hd_buf, 20); count = read(thefd, ((char*)&vochd)+20, sizeof(vochd) - 20); fprintf(stderr, "Format version %d.%d\n", vochd.Version>>8, vochd.Version&0xFF); if (vochd.IDCode != (~vochd.Version+0x1234)) fprintf(stderr, "Odd - version mismatch - %d != %d\n", vochd.IDCode, ~vochd.Version+0x1234); if(sizeof(vochd) < vochd.BlockOffset) { int off = vochd.BlockOffset - sizeof(vochd); char *junk; junk = (char*) malloc(off); read(thefd, junk, off); } while(!quit) { if ((read(thefd, (char*)&ccblock, sizeof(ccblock))) == -1) { if (debug) fprintf(stderr, "Terminating\n"); diskread(thefd, -1, NULL, 1, speed, bits, stereo); quit = 1; continue; } if (debug) fprintf(stderr, "Block of type %d found\n", ccblock.BlockID); switch(ccblock.BlockID) { case 1: { blockT1 tblock; read(thefd, (char*)&tblock, sizeof(tblock)); if(tblock.PackMethod != 0) Die("Non PCM VOC block"); if (lastblocktype != 8) { speed = 256000000/(65536 - (tblock.TimeConstant << 8)); bits = 8; stereo = 0; } bytecount = DATALEN(ccblock) -2; diskread(thefd, bytecount, NULL, 0, speed, bits, stereo); lastblocktype = 1; } break; case 8: { blockT8 tblock; read(thefd, (char*)&tblock, sizeof(tblock)); if(tblock.PackMethod != 0) Die("Non PCM VOC block"); speed = 256000000/(65536 - tblock.TimeConstant); bits = 8; stereo = tblock.VoiceMode; if (stereo) speed >>=1; lastblocktype = 8; } break; case 9: { blockT9 tblock; read(thefd, (char*)&tblock, sizeof(tblock)); if(tblock.Format != 0 && tblock.Format != 4) Die("Non PCM VOC block"); speed = tblock.SamplesPerSec; bits = tblock.BitsPerSample; stereo = tblock.Channels - 1; bytecount = DATALEN(ccblock) - 12; diskread(thefd, bytecount, NULL, 0, speed, bits, stereo); lastblocktype = 9; } break; case 0: if (debug) fprintf(stderr, "Terminating\n"); diskread(thefd, -1, NULL, 1, speed, bits, stereo); quit = 1; break; case 6: inloop = 1; read(thefd, (char*)&loop_times, 2); loop_times++; if (debug) fprintf(stderr, "Beginning loop %d\n", loop_times); loop_pos = lseek(thefd, 0, SEEK_CUR); if(loop_pos == -1) { fprintf(stderr, "Input not seekable - loop will only be played once\n"); loop_times = 1; } lastblocktype = ccblock.BlockID; break; case 7: if(!inloop) { fprintf(stderr, "Loop end with no loop start - ignoring\n"); break; } if(loop_times != 0xFFFF) --loop_times; if(loop_times) { if (debug) fprintf(stderr, "Looping...\n"); lseek(thefd, loop_pos, SEEK_SET); } else inloop = 0; lastblocktype = ccblock.BlockID; break; default: { int rd = 0, trgt = BUFSIZ; char junkbuf[BUFSIZ]; fprintf(stderr, "Ignored\n"); bytecount = DATALEN(ccblock); while(rd < bytecount) { if (rd + trgt > bytecount) trgt = bytecount - rd; count = read(thefd, junkbuf, trgt); if (count < 0) ErrDie("read"); if (count == 0) Die("premature eof in input"); rd += count; } lastblocktype = ccblock.BlockID; } break; } } } bplay-0.991/bplay.lsm0100644000411700001440000000114007161510300013640 0ustar davidmusersBegin3 Title: bplay Version: 0.991 Entered-date: Mon Sep 18, 2000 Description: A buffered audio play/record program. Handles WAV, VOC and raw sound files. Should allow glitch-free recording at 44100kHz/16 bit stereo on most hardware. Keywords: audio play record Author: davidm@amberdata.demon.co.uk (David Monro) Maintained-by: davidm@amberdata.demon.co.uk (David Monro) Primary-site: http://www.amberdata.demon.co.uk/bplay/ 20kB bplay-0.991.tar.gz 1kB bplay.lsm Platform: Linux and supported sound card Copying-policy: GPL End bplay-0.991/brec.10100644000411700001440000000002107012115311013004 0ustar davidmusers.so man1/bplay.1 bplay-0.991/fmtheaders.h0100644000411700001440000000350707047004326014331 0ustar davidmusers#ifndef _FMTHEADERS_H #define _FMTHEADERS_H 1 #include /* Definitions for .VOC files */ #define VOC_MAGIC "Creative Voice File\032" #define DATALEN(bp) ((u_int)(bp.BlockLen[0]) | \ ((u_int)(bp.BlockLen[1]) << 8) | \ ((u_int)(bp.BlockLen[2]) << 16) ) typedef struct vochead { u_char Magic[20]; /* must be VOC_MAGIC */ u_short BlockOffset; /* Offset to first block from top of file */ u_short Version; /* VOC-file version */ u_short IDCode; /* complement of version + 0x1234 */ } vochead; typedef struct blockTC { u_char BlockID; u_char BlockLen[3]; /* low, mid, high byte of length of rest of block */ } blockTC; typedef struct blockT1 { u_char TimeConstant; u_char PackMethod; } blockT1; typedef struct blockT8 { u_short TimeConstant; u_char PackMethod; u_char VoiceMode; } blockT8; typedef struct blockT9 { u_int SamplesPerSec; u_char BitsPerSample; u_char Channels; u_short Format; u_char reserved[4]; } blockT9; /* Definitions for Microsoft WAVE format */ /* it's in chunks like .voc and AMIGA iff, but my source say there are in only in this combination, so I combined them in one header; it works on all WAVE-file I have */ typedef struct wavhead { u_int main_chunk; /* 'RIFF' */ u_int length; /* Length of rest of file */ u_int chunk_type; /* 'WAVE' */ u_int sub_chunk; /* 'fmt ' */ u_int sc_len; /* length of sub_chunk, =16 (rest of chunk) */ u_short format; /* should be 1 for PCM-code */ u_short modus; /* 1 Mono, 2 Stereo */ u_int sample_fq; /* frequence of sample */ u_int byte_p_sec; u_short byte_p_spl; /* samplesize; 1 or 2 bytes */ u_short bit_p_spl; /* 8, 12 or 16 bit */ u_int data_chunk; /* 'data' */ u_int data_length; /* samplecount (lenth of rest of block?)*/ } wavhead; #endif bplay-0.991/shmbuf.c0100644000411700001440000002610607047004326013466 0ustar davidmusers/* ** ** bplay/shmbuf.c (C) David Monro 1996 ** ** Copyright under the GPL - see the file COPYING in this directory ** ** 1999.02.09. huba ** Fixed a bug in semaphore handling ** */ #include #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBC__ /* Nasty hack */ #ifndef SEMMSL #define SEMMSL 32 #endif #endif /* The default size of the big array */ /* (currently 256K - nearly 1.5 sec at max rate) */ #define DEFAULTBUFFSIZE 0x040000 /* Types */ typedef struct blockinf_t { int count; /* How many bytes in this buffer */ int last; /* Should we terminate after this buffer? */ int setit; /* Should we re-set the audio parameters to be the ones here? */ int speed; int bits; int stereo; } blockinf_t; #ifdef _SEM_SEMUN_UNDEFINED /* is semun defined in ? */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short int *array; /* array for GETALL & SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #endif /* Globals */ int bigbuffsize = DEFAULTBUFFSIZE; /* Statics - mostly shared memory etc */ static int shmid, shmid2, *disksemid, *sndsemid; static char *bigbuff; static char **buffarr; static int numbuffs, numsemblks; static blockinf_t *buffinf; /* prototypes */ void cleanupsems(void); static void sighandler(int i); /* Extern globals */ extern int abuf_size; extern int audio; extern char *progname; extern pid_t pid; extern int recorder; extern int debug; /* extern prototypes */ extern void ErrDie(char *err); extern void snd_parm(int speed, int bits, int stereo); extern void sync_audio(void); extern void cleanup_audio(void); void init_shm(void) { int i; /* Round up to a multiple of abuf_size */ if (bigbuffsize % abuf_size != 0) { bigbuffsize = ((bigbuffsize / abuf_size) +1) * abuf_size; } /* Create, attach and mark for death the big buffer */ shmid = shmget(IPC_PRIVATE, bigbuffsize, IPC_EXCL | IPC_CREAT | 0600); if (shmid == -1) ErrDie("shmget"); bigbuff = shmat(shmid, IPC_RMID, SHM_RND); if (bigbuff == (char*)-1) { perror("shmat"); if(shmctl(shmid, IPC_RMID, NULL)) perror("shmctl"); exit(-1); } if(shmctl(shmid, IPC_RMID, NULL)) ErrDie("shmctl"); /* Create an array of pointers. Point them at equally spaced ** chunks in the main buffer, to give lots of smaller buffers */ numbuffs = bigbuffsize/abuf_size; buffarr = (char**)malloc(numbuffs*sizeof(char*)); for (i=0; i 0) ) { numwr += count; tmpptr += count; } /* Mark the buffer as clean */ up(sndsemid, cbuff); /* If the block was marked as the last one, stop */ if (buffinf[cbuff].last) break; /* Advance the pointer */ cbuff++; cbuff%=numbuffs; } /* Tidy up and exit, we are being waited for */ close(outfd); exit(0); } else { /* Parent reads audio */ int cbuff = 0, totalrd = 0; while (totalrd < totalcount) { int trgt, count, numrd; char *tmpptr; trgt = totalcount - totalrd; if (trgt > abuf_size) trgt = abuf_size; /* Get the buffer. Blocks until OK to do so */ down(sndsemid, cbuff); /* Read a block of data */ numrd = 0; tmpptr = buffarr[cbuff]; while( (numrd < trgt) && ((count = read(audio, tmpptr, trgt - numrd)) > 0) ) { numrd += count; tmpptr += count; } /* Update the count for this block */ buffinf[cbuff].count = numrd; /* Update the amount done */ totalrd += numrd; /* Tell the reader to stop if needed */ if ((totalrd >= totalcount) && terminate) buffinf[cbuff].last = 1; /* Mark the buffer dirty */ up(disksemid, cbuff); /* Update the counter */ cbuff++; cbuff%=numbuffs; } /* Tidy up and wait for the child */ close(audio); wait(NULL); /* Free all the semaphores */ cleanupsems(); } } void diskread(int infd, int totalplay, char hd_buf[20], int terminate, int speed, int bits, int stereo) { int count, i, limited = 0; char *tmppt; int numread, totalread = 0; int first = 1; static int triggered = 0; /* Have we let the writer go? */ static int cbuff = 0; /* Which buffer */ if (totalplay) limited = 1; if (totalplay == -1) { totalplay = 0; limited = 1; } while (1) { int trgt; /* Wait for a clean buffer */ down(disksemid, cbuff); /* Read from the input */ numread = 0; trgt = abuf_size; if (limited && (totalread + trgt > totalplay)) trgt = totalplay - totalread; tmppt = buffarr[cbuff]; if(first && trgt) { buffinf[cbuff].setit = 1; buffinf[cbuff].speed = speed; buffinf[cbuff].bits = bits; buffinf[cbuff].stereo = stereo; if(hd_buf) { memcpy(tmppt, hd_buf, 20); tmppt += 20; numread = 20; } first = 0; } while ( (numread < trgt) && ((count = read(infd, tmppt, trgt - numread)) != 0) ) { tmppt += count; numread += count; } if (debug >= 2) fprintf(stderr, "in:%d, %d\n", cbuff, numread); /* Update the count for this block */ buffinf[cbuff].count = numread; totalread += numread; /* Was it our last block? */ if (numread < abuf_size) break; if(triggered) up(sndsemid, cbuff); else if(cbuff == numbuffs-1) { if (debug) fprintf(stderr, "Triggering (in loop)\n"); for(i = 0; i < numbuffs; i++) up(sndsemid,i); triggered = 1; } /* Update counter */ cbuff++; cbuff %= numbuffs; } /* Finish off this set of buffers */ if(terminate) { buffinf[cbuff].last = 1; if(!triggered) { if (debug) fprintf(stderr, "Triggering (after loop, partial)\n"); /* If it wasn't triggered, we haven't filled past cbuff */ for(i = 0; i < cbuff; i++) up(sndsemid, i); } up(sndsemid, cbuff); } else if((!triggered) && (cbuff == numbuffs-1)) { if (debug) fprintf(stderr, "Triggering (after loop, full)\n"); for(i = 0; i < numbuffs; i++) up(sndsemid,i); triggered = 1; } else if(triggered) up(sndsemid,cbuff); cbuff++; cbuff %= numbuffs; } volatile void audiowrite(void) { int cbuff = 0, count, numwr, trgt; char *tmpptr; /* Uncatch the signals, so we don't clean up twice */ signal(SIGINT, SIG_DFL); /* Child process writes the audio */ while(1) { /* Wait for dirty buffer */ down(sndsemid, cbuff); /* Spit to the audio device */ if(buffinf[cbuff].setit) { snd_parm(buffinf[cbuff].speed, buffinf[cbuff].bits, buffinf[cbuff].stereo); buffinf[cbuff].setit = 0; } trgt = buffinf[cbuff].count; numwr = 0; tmpptr = buffarr[cbuff]; while ( (numwr < trgt) && ((count = write(audio, tmpptr, trgt - numwr)) > 0) ) { if (count == -1) ErrDie("write"); numwr += count; tmpptr += count; } if (debug >= 2) fprintf(stderr, "out:%d, %d\n", cbuff, numwr); /* Was it the last buffer? */ if (buffinf[cbuff].last) { up(disksemid, cbuff); /* Not really needed */ break; } /* Mark as clean */ up(disksemid, cbuff); /* Update counter */ cbuff++; cbuff %= numbuffs; } /* Tidy up and be reaped */ sync_audio(); close(audio); exit(0); } void initsems(int disks, int snds) { int i,j; union semun dsu, ssu; dsu.val = disks; ssu.val = snds; for (i=0;i #include #include #include #include #include #include #define AUDIO "/dev/dsp" /* Globals */ int audio, abuf_size, fmt_mask; char *audev = AUDIO; /* Prototypes */ void sync_audio(void); void cleanup_audio(void); /* Extern globals */ extern char *progname; extern int debug; /* Extern prototypes */ extern void ErrDie(char *err); extern void Die(char *err); void init_sound(int recorder) { /* Attempt to open the audio device */ audio = open(audev, (recorder)? O_RDONLY : O_WRONLY); if (audio == -1) ErrDie(audev); #if 1 if (ioctl(audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size) < 0) ErrDie(audev); if (debug) fprintf(stderr, "abuf_size = %d\n", abuf_size); #if 0 if (abuf_size < 1024 || abuf_size > 65536) Die("invalid audio buffer size"); #else if (abuf_size < 4096) abuf_size = 4096; /* Seems reasonable */ #endif #else abuf_size = 65536; #endif #if 1 if (ioctl(audio, SNDCTL_DSP_GETFMTS, &fmt_mask) < 0) ErrDie(audev); #endif } void snd_parm(int speed, int bits, int stereo) { static int oldspeed = -1, oldbits = -1, oldstereo = -1; if ((speed != oldspeed) || (bits != oldbits) || (stereo != oldstereo)) { /* Sync the dsp - otherwise strange things may happen */ #ifdef DEBUG fprintf(stderr, " - syncing - "); #endif sync_audio(); /* Set the sample speed, size and stereoness */ if (ioctl(audio, SNDCTL_DSP_SAMPLESIZE, &bits) < 0) ErrDie(audev); if (ioctl(audio, SNDCTL_DSP_STEREO, &stereo) < 0) ErrDie(audev); if (ioctl(audio, SNDCTL_DSP_SPEED, &speed) < 0) ErrDie(audev); } oldspeed = speed; oldbits = bits; oldstereo = stereo; } void sync_audio(void) { if (ioctl (audio, SNDCTL_DSP_SYNC, NULL) < 0) ErrDie(audev); } void cleanup_audio(void) { if (ioctl (audio, SNDCTL_DSP_RESET) < 0) ErrDie(audev); close(audio); }