gramofile-1.6/ 40755 1751 144 0 7070217735 12154 5ustar costarusersgramofile-1.6/README100644 1751 144 34200 7070217330 13137 0ustar costarusersGramoFile - Gramophone records to CDs, Tick Reduction, Track Splitting Copyright (C) 1998 J.A. Bezemer 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., 675 Mass Ave, Cambridge, MA 02139, USA. INTRODUCTION / OVERVIEW This is version 1.6 of GramoFile, released March 28, 2000. With this program you can record audio of (for example) gramophone records, process the signal and listen to the results. Because sound files of the .WAV-format are used, it is possible to exchange the files with many other programs. Cdrecord(1) can burn CD-Recordables of these, so you can make CDs with the music of your favorite records. The user interface of GramoFile has a windows-like look-and-feel, making it fairly easy to use. One of the most important parts of GramoFile is the ability to process digital audio signals. Through the application of several filters it is possible to accomplish a significant reduction of disturbances like ticks and scratches. These filters have been programmed in such a fashion that they can be applied in a random order (and multiple times) in a single run, thus evading the use of temporary files. There is a possibility of adjusting the properties of each filter independently, so in every situation an optimal result can be achieved. Another interesting feature is the track splitting. Just make one .wav file of an entire side of an record and GramoFile will detect where the individual tracks are located. This happens fully automatically, without need to set any options. More experienced users may fine-tune the algorithm, or change the detected track starts and ends, but generally that will not be necessary. Track-times are saved in an editable (plaintext) .tracks file, that will be used during the signal processing to produce one processed .wav file for each individual track. To record and play .wav files, modified versions of brec(1) and bplay(1) by David Monro are used. These programs provide buffered recording and playback, so all will go well even on a highly loaded system. Both programs have been supplied with the standard GramoFile user interface. Brec also got a `graphical' peak level meter, and bplay a running time display. INSTALLATION The file gramofile-1.6.tar.gz, available at http://panic.et.tudelft.nl/~costar/gramofile/ contains the complete C source code of the GramoFile program. To unzip it, type tar xzvf gramofile-1.6.tar.gz or, alternatively, gunzip gramofile-1.6.tar.gz tar xvf gramofile-1.6.tar Change the working directory to the newly created directory gramofile-1.6 and issue the command make to start the compilation process. Note for all 64-bit systems: when your `long'-type vars are 64 bit, .wav headers are not read/written correctly (=bug). If you replace every `long' with an `int' (or other 32-bit type), everything should be fine. Note for non-Linux systems (like IRIX): check the Makefile first, you probably have to uncomment some lines. Also, you probably won't have sound recording/playing capabilities. If you experience portability problems, please fix them and send me a nice patch ;-) Note for Solaris: there seem to be problems with the scandir() function not being present. It might be in the "SunOS/BSD Compatibility Library" (don't ask me...) Update: there is a scandir replacement in sun_scandir.c. To use it, add it to SRCS in the Makefile; also add a line int alphasort(struct dirent **d1, struct dirent **d2); in the beginning of dirfilemenu.c. Note that the Makefile is written especially for use with GNU `make' (sometimes called `gmake'). That `make' implementation regards #included files as targets, enabling us to create and include dependency information in the same run as compiling the program. That's also the reason why those "file not found" messages appear: these files will be made and included automatically. More information on the GNU programs may be acquired by pointing your browser to http://www.gnu.org. If you don't have the GNU version of `make', you can simply comment the `include' line out; it is primarily useful during development activities. To run the GramoFile program, issue the command gramofile USAGE NOTES General: - The GramoFile program uses customized versions of `bplay' and `brec', named `bplay_gramo' and `brec_gramo'. These programs are started from within the `gramofile' program with just that names (no directories). That means that they should be located in a directory that is in your search path ($PATH), e.g. /usr/local/bin. But if you don't want (or are not allowed) to put them there, add `.' (current directory) to your search path, set your working directory to the GramoFile directory and start the program with just `gramofile' (the './' as in `./gramofile' should not be necessary). Note: this is a known bug (if you like, send me a nice patch ;-). - Information on CD burning issues may be found on: http://www.fokus.gmd.de/nthp/employees/schilling/cdb.html Record an audio file: - Use the `Mixer' button in the file selection screen to start xmixer(1). Make sure the appropriate input (and no other) is selected for recording. SoundBlaster(TM) owners: note that treble and bass settings affect recording! I found SB16PnP works better with both sliders at 50%, Richard Robinson reports his SB64Gold requires treble 65%, bass 45%. - If you choose `Cancel' during the "Press Enter to start recording" box, the selected audio file has already been created/truncated, and will have zero length. So if you were overwriting a file, it was already deleted before this dialog box appeared. - The length as reported in the .wav header of the recorded file is not correct. This is because brec doesn't update the header if the recording was stopped prematurely (by pressing Enter). If you have problems with this, simply process the sound file with the `Copy Only' filter; the output file has the correct length in the .wav header. Alternatively, you can make some additions to the bplay/brec source code... - To get the best out of track location, you shouldn't record too much silence before the first & after the last track. 3-5 seconds silence will probably get the best results. Also, I don't recommend recording two sides of a record in a single (long) sound file; make two files instead. - If you want to view the input signal level without recording to a sound file, try recording to /dev/null ;-) Copy audio from an audio-CD to a file: - This option is not yet implemented. There are some plans to program a uniform user interface to the cdda2wav(1) program here. You have to use the bare program for this moment. More info: ftp://ftp.gwdg.de/pub/linux/misc/cdda2wav Alternatively, you can try the cdparanoia(1) program: http://www.mit.edu/afs/sipb/user/xiphmont/cdparanoia/ Track location: - Track location information is saved in a plaintext file with the same name as the audio file but appended with ".tracks", as in "firaru_side1.wav.tracks". You may view/use/edit this file as you please. - The ".tracks" file will be written in the same directory as the sound file. If that directory is not writable, create a symlink to the sound file from a directory like "/tmp/gramofile", and use that `new' sound file instead. - For more information on the algorithms used, and the procedure you should follow to get optimal track location, see the Tracksplit2.txt file. - If you want to view the (optionally generated) .med or .sor files with gnuplot, you may use a gnuplot-command like: plot '/dir/filename.wav.med' using 2 smooth unique or to view only a certain interval: plot [1000:2000] '/dir/filename.wav.med' using 2 smooth unique Process the audio signal: - If you choose to split tracks, numbered files will be created with the `output filename' as base. If the `output filename' is entered as "/tmp/test.wav", files like "/tmp/test01.wav", "/tmp/test02.wav", etc. will be created. You will be warned if a file "/tmp/test.wav" exists, although it will not be overwritten, but the 01, 02, etc. files will be overwritten without your consent. Be warned! (This is also a bug.) - You can also pipe the output data directly to some command that accepts .wav-format input via stdin, when using a "filename" like `|progname args outfile.ext'. Popular example: `|bladeenc -quiet STDIN processed.mp3'. If track splitting is done, there _must_ be something like a filename at the end of the line; e.g. the `.mp3' will be replaced by `01.wav', `02.wav' etc. (Always `.wav', this is a bug.) A new command is executed for each track. Note: 1) if the name can't be the last item on the line, write a shell-script wrapper; 2) the output file must be read once and in-order, which some programs (like LAME!) can't do. Easily fixed with a script that first saves the data to a .wav tempfile, then performs the wanted action, and finally removes the tempfile. - For more information on some of the implemented filtering algorithms, and the implementation of new filters, see the Signproc.txt file. - Known problem: the algorithm for the Conditional Median Filter II is not optimal. With certain musical instruments and tones, the sound might get distorted by an annoying `shot noise'. This is because those instruments produce sawtooth-like sound waves, containing lots of high frequencies. The `normal' highpass filter used by the CMF II detects these high frequencies as ticks. You can solve this problem in two ways. First, you may try to increase the tick detection threshold (second threshold) to for example 7000 or higher. Second, you can try to use an alternative highpass filter, by #defineing either FOURTH_ORDER or SIXTH_ORDER in signpr_cmf2.c. These highpasses enhance very high frequencies, so real (short) ticks are detected better, even when using higher thresholds. - Hint: if you are doing mono 78's, it might be profitable to use a convert-to-mono filter first. You'll still get a stereo output file, but with both channels identical. Use sox(1) or the like to convert it to a real mono file. Write an audio CD: - This option is not yet implemented. There are some plans to program a uniform user interface to the cdrecord(1) program here. You have to use the bare program for this moment. Try something like cdrecord -v speed=2 dev=0,2,0 -audio -pad track01.wav track02.wav ... Actual information on cdrecord may be found on: http://www.fokus.gmd.de/research/cc/glone/employees/ joerg.schilling/private/cdrecord.html If you like a graphical front-end to write CD's, try the xcdroast program: http://www.fh-muenchen.de/rz/xcdroast (Note: you may have to use the wav2cdr utility.) Play an audio file: - Make sure the PCM and Master volumes are set high enough, and your speakers are plugged in correctly and turned on ;-) - I find the `Track' field to be extremely useful. If I've had located tracks, I use one xterm with GramoFile, and another one with a editor with the .tracks file. If I don't agree with the automatic detection, I change the .tracks file, save it, and listen to the new beginning and end in GramoFile. Note that the .tracks file is reread every time you press B, E, F, A or Enter. - To notice differences between filtered/non-filtered sound files, you've got to have speakers that are fit for that purpose. I've found that big speakers are quite `slow' and have a natural ability to reduce tick volume, because they can't keep up with the speed. To be able to hear the ticks as they are, little speakers are most useful, but small, "low-quality" headphones (as used with cheap portable audio) are even better. BUGS Plenty. Like max string lengths, un-free-d memory, bad error-handling. But if you don't do too extraordinary things, you shouldn't notice anything. The source code is definitely not portable (well, IRIX runs fine...). There is no manpage. There are no commandline options - so why bother about a manpage? Only text mode. Screensize is always 80x24. Only CD-quality .wav files are accepted (well, that was the purpose, wasn't it?). There are major language errors everywhere (please send me corrections). `make install' doesn't work. But I expect too have only very limited time to do something about all them bugs. You may report bugs at any time, direct them to J.A.Bezemer@ITS.TUDelft.NL. Completely worked out fixes are greatly appreciated ;-) AUTHORS GramoFile was developed by Anne Bezemer and Ton Le, students of the department of Information Technology and Systems (ITS), sub-department of Electrical Engineering (ET) of the Delft University of Technology (TU Delft). The first major part was written during the `Integraal Project Practicum' (IPP) in the spring of 1998. However, development has continued unofficially thereafter (so now we have track splitting operational :). Total development time is about 7 weeks full-time. The following persons have been very helpful and/or supportive during the `official' part of the project: A.P. Thijssen, E.A. Hendriks, A. Redert, J.A.H. Snelders, L. Meijs and R.J. de Gruijl. Other persons are mentioned in the ChangeLog file. Any questions, comments, suggestions and bugfixes may be mailed to J.A.Bezemer@ITS.TUDelft.NL (or try costar@panic.et.tudelft.nl or mcbzmr@dds.nl). But please understand that I'm a student with very limited time to do the more pleasant things in life. Have fun! -- All mentioned trademarks and registered trademarks are the property of their respective owners. gramofile-1.6/Makefile100644 1751 144 4237 7070212336 13707 0ustar costarusersPROG = gramofile SRCS = boxes.c buttons.c checkfile.c dirfilemenu.c errorwindow.c \ gramofile.c mainmenu.c reclp_filenm.c reclp_main.c scrollmenu.c \ stringinput.c textwindow.c yesnowindow.c clrscr.c helpline.c \ signpr_main.c signpr_infilenm.c signpr_outfilenm.c \ signpr_general.c signpr_median.c signpr_filtmenu.c signpr_wav.c \ secshms.c playwav.c signpr_cmf.c signpr_mean.c signpr_doubmed.c \ splashscr.c tracksplit.c tracksplit_filenm.c \ tracksplit_parammenu.c signpr_cmf2.c signpr_rms.c signpr_copy.c \ signpr_exper.c endian.c signpr_mono.c signpr_l1fit.c OBJS = $(SRCS:.c=.o) SHELL = /bin/sh CC = gcc LDFLAGS = ########## CHOOSE YOUR ARCHITECTURE: (NOTE: also see bplaysrc/Makefile!) # For Linux (and maybe others), use these: CFLAGS = -Wall -O2 -DTURBO_MEDIAN -DTURBO_BUFFER DEPS = $(OBJS) makebplay LIBS = -lncurses -lm COPY_A = -a # For FreeBSD (and maybe others), use these: #CFLAGS = -Wall -O2 -DTURBO_MEDIAN -DTURBO_BUFFER #DEPS = $(OBJS) makebplay #LIBS = -lncurses -lm #COPY_A = -p # For IRIX (and maybe others), use these: #CFLAGS = -Wall -O2 -DTURBO_MEDIAN -DTURBO_BUFFER -DSWAP_ENDIAN -DOLD_CURSES #DEPS = $(OBJS) #LIBS = -lcurses -lm #COPY_A = -a ########## $(PROG): $(DEPS) $(CC) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) @echo '' @echo '' @echo '' @echo " If you're one of those that didn't read the README, please do so now." @echo '' makebplay: yesnowindow.o boxes.o buttons.o textwindow.o errorwindow.o \ clrscr.o secshms.o $(MAKE) -C bplaysrc -rm bplay_gramo brec_gramo cp $(COPY_A) bplaysrc/bplay ./bplay_gramo ln -s bplay_gramo brec_gramo .PHONY: clean clean: $(MAKE) -C bplaysrc clean -rm -f gramofile bplay_gramo brec_gramo *.o *.d *~ .PHONY: indent indent: indent *.c *.h #%.d: %.c - according to 'info make', doesn't work # $(SHELL) -ec '$(CC) -MM $(CPPFLAGS) $< \ # | sed '\''s/\($*\)\.o[ :]*/\1 $@/g'\'' > $@' # # 'some.o: some.c other.h' ==> 'some some.dsome.c other.h' %.d: %.c $(SHELL) -ec '$(CC) -MM $(CPPFLAGS) $< \ | sed '\''s/\($*\)\.o/& $@/g'\'' > $@' # # 'some.o: some.c other.h' ==> 'some.o some.d: some.c other.h' => OK include $(SRCS:.c=.d) gramofile-1.6/boxes.c100644 1751 144 1357 7070217415 13536 0ustar costarusers/* Boxes * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "boxes.h" #ifndef OLD_CURSES #include #else #include #endif void mybox (int y, int x, int h, int w) /* Draws a box, top left = (y,x), heigth = h, width = w. */ { mvaddch (y, x, ACS_ULCORNER); mvaddch (y, x + w - 1, ACS_URCORNER); mvaddch (y + h - 1, x, ACS_LLCORNER); mvaddch (y + h - 1, x + w - 1, ACS_LRCORNER); move (y, x + 1); hline (ACS_HLINE, w - 2); move (y + 1, x + w - 1); vline (ACS_VLINE, h - 2); move (y + h - 1, x + 1); hline (ACS_HLINE, w - 2); move (y + 1, x); vline (ACS_VLINE, h - 2); } gramofile-1.6/boxes.h100644 1751 144 571 7070217416 13521 0ustar costarusers/* Boxes - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_BOXES_H #define HAVE_BOXES_H extern void mybox (int y, int x, int h, int w); /* Draws a box, top left = (y,x), heigth = h, width = w. */ #endif /* HAVE_BOXES_H */ gramofile-1.6/bplaysrc/ 40755 1751 144 0 7070217735 13773 5ustar costarusersgramofile-1.6/bplaysrc/COPYING100644 1751 144 43076 5027140160 15140 0ustar costarusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, 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 Appendix: 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) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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. gramofile-1.6/bplaysrc/Makefile100644 1751 144 1371 7067750636 15542 0ustar costarusers# Modified for use with GramoFile, June 1998, J.A. Bezemer PROG = bplay OBJS = bplay.o sndfunc.o shmbuf.o ../yesnowindow.o ../boxes.o \ ../buttons.o ../textwindow.o ../errorwindow.o \ ../clrscr.o ../secshms.o #SRCS = bplay.c sndfunc.o shmbuf.c CC = gcc LDFLAGS = ########## CHOOSE YOUR ARCHITECTURE: (NOTE: also see ../Makefile!) # For Linux (and maybe others), use these: CFLAGS = -Wall -O2 -DUSEBUFFLOCK -DREDHAT50 -DLP2CD -DVUMETER # -DDEBUG LIBS = -lncurses # For FreeBSD (and maybe others), use these: #CFLAGS = -Wall -O2 -DLP2CD -DVUMETER # -DDEBUG #LIBS = -lncurses # -DOLD_CURSES can be used here, too. ########## $(PROG): $(OBJS) $(CC) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) ln -sf bplay brec clean: rm -f bplay brec *.o gramofile-1.6/bplaysrc/README.orig100644 1751 144 6051 6277702646 15721 0ustar costarusersThis is version 0.96 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. 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. 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). When recording there is currently a limit of 16MB files when recording in WAV format. 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 older player programs. 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 stero, 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 debuggin 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. Other things to tweak: the size of the share buffer is 256K by default, or just under 1.5 seconds of CD audio. Change BIGBUFFSIZE at the top of shmbuf.c to change this. It should always be a multiple of abuf_size, which is set on line 42 of sndfunc.c. This is set to 64K, which works well. 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 top 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 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@cs.usyd.edu.au gramofile-1.6/bplaysrc/bplay.1100644 1751 144 4144 6277702646 15274 0ustar costarusers.TH BPLAY 1 "10 June 1994" .SH NAME bplay, brec \- buffered sound recording/playing .SH SYNOPSIS .B bplay [\-S] [\-s speed] [\-b bits] [[\-t secs] | [\-T samples]] [[\-j secs] | [\-J samples]] [file] .sp .B brec [\-S] [\-s speed] [\-b bits] [[\-t secs] | [\-T samples]] [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 eventually 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 "\-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. .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 newer 1.20 version of the format, which older player programs may choke on. These limitations should be fixed in v1.0. .PP 12 bit samples are not currently supported. This may be fixed if someone tells me the format used for 12 bit samples - my sound card doesn't support 12 bit mode. I'm not sure that the sound driver supports them either though. .PP This program runs setuid root. This is required because it uses 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@cs.usyd.edu.au .PP The option parsing code was originally taken from .B vplay to maintian compatability. gramofile-1.6/bplaysrc/bplay.c100644 1751 144 37343 7067752652 15405 0ustar costarusers/* ** brec/bplay.c (C) David Monro 1996 ** ** Copyright under the GPL - see the file COPYING in this directory ** ** Adapted by J.A. Bezemer for use with GramoFile - July 1998 */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __FreeBSD__ #include #else #include #endif #include "fmtheaders.h" #include "../yesnowindow.h" void init_curses(void); void finish_curses(int sig); #include "../errorwindow.h" #include "../clrscr.h" #include "../reclp_main.h" #ifndef OLD_CURSES #include #else #include #endif /* 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; /* Prototypes */ #ifdef linux /* This is in libc, but not in the header files. -- but it IS in Red Hat 5.0.... Libc6? well i'd guess its not in the headers because its nonstandard, i.e. (probably) only exists on linux... -nox */ #ifndef REDHAT50 extern char *basename(char *name); #endif #endif void Usage(void); void ErrDie(char *err); void Die(char *err); void getbcount(int speed, int bits, int stereo, long *bcount, long timelim, long samplim, long timejmp, long sampjmp, long *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, long timelim, long samplim, long timejmp, long sampjmp ); /* was: 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 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, long bcount, int terminate); extern void diskread(int outfd, long bcount, long skipped, 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); void main(int argc, char *argv[]) { int recorder = 0; /* 1 if recording, 0 else */ int thefd; /* The file descriptor */ int speed, bits, stereo; /* Audio parameters */ long timelim; /* Recording time in secs */ long samplim; /* Recording time in samples */ long timejmp; /* Skip time in secs */ long sampjmp; /* Skip time in samples */ long bcount; /* Number of bytes to record */ long 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 */ init_curses(); #ifdef linux progname = basename(argv[0]); /* For errors */ #else progname = strrchr(argv[0], '/'); /* Replacement for e.g. FreeBSD */ if (!progname || !*++progname) progname = argv[0]; #endif /* Ok, find out if we record or play */ if (strcmp(progname, "brec_gramo") == 0) recorder = 1; else { if (!(strcmp(progname, "bplay_gramo") == 0)) Die("must be called as bplay_gramo or brec_gramo"); 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:rvw")) != -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 = atol(optarg); break; case 'T': samplim = atol(optarg); break; case 'j': timejmp = atol(optarg); break; case 'J': sampjmp = atol(optarg); break; case 'r': filetype = F_RAW; break; case 'v': filetype = F_VOC; break; case 'w': filetype = F_WAV; break; default: Usage(); } } #if 1 /* This program is either set-uid or run by root (I hope...) */ if (setpriority(PRIO_PROCESS, 0, -20) == -1) #ifndef LP2CD fprintf(stderr, "%s: setpriority: %s: continuing anyway\n", progname, strerror(errno)) #endif ; #endif /* 1 */ /* Drop out of suid mode before we open any files */ if(setreuid(geteuid(), getuid()) == -1) { #ifndef LP2CD fprintf(stderr, "%s: setreuid: %s: continuing anyway\n", progname, strerror(errno)); fprintf(stderr, "real uid = %d, effective uid = %d\n", getuid(), geteuid()); #endif } /* 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(); /* Call the appropriate routine */ if (recorder) { getbcount(speed, bits, stereo, &bcount, timelim, samplim, timejmp, sampjmp, &bjump); #ifdef DEBUG fprintf(stderr, "bcount: %ld\n", bcount); #endif /* By CS: */ clearscreen(RECLP_HEADERTEXT); if (!yesno_window( "Press Enter to start recording.", " Start ", " Cancel ", 1)) { close(thefd); close(audio); finish_curses(0); } header(RECLP_HEADERTEXT); error_window_display( "Press Enter to stop recording.", " Stop "); /* End CS */ if (filetype == F_UNKNOWN) filetype = F_RAW; /* Change to change default */ switch(filetype) { case F_WAV: /* Spit out header here... */ #ifndef LP2CD fprintf(stderr, "Writing MS WAV sound file"); #endif { 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; /* Correction by J.A. Bezemer: */ header.byte_p_spl = ((bits > 8)? 2:1)*(stereo+1); /* was: header.byte_p_spl = (bits > 8)? 2: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) #ifndef LP2CD fprintf(stderr, "Writing raw sound file") #endif ; #ifndef LP2CD fprintf(stderr, ", %dHz, %dbit, %s\n", speed, bits, (stereo)? "stereo":""); #endif 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; pid_t pid; 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; if (filetype == F_RAW) playraw(thefd, hd_buf, speed, bits, stereo); else { /* 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, timelim, samplim, timejmp, sampjmp); else /* Assume raw data */ playraw(thefd, hd_buf, speed, bits, stereo); } wait(NULL); cleanupsems(); } finish_curses(0); } void Usage(void) { int i; printw( "Usage: %s [-S] [-s Hz] [-b 8|16] [-t secs] [-r|-v|-w] [filename]\n", progname); printw("\nPress any key..."); refresh(); i = getch(); finish_curses(-1); exit(1); } void ErrDie(char * err) { char string[500]; sprintf(string, "%s: %s", err, strerror(errno)); error_window(string); finish_curses(-1); exit(-1); } void Die(char * err) { char string[500]; sprintf(string, "%s", err); error_window(string); finish_curses(-1); exit(-1); } void getbcount(int speed, int bits, int stereo, long *bcount, long timelim, long samplim, long timejmp, long sampjmp, long *bjump) { if(timelim) { /* fprintf(stderr,"tl=%ld, spd=%d, bits=%d\n",timelim, speed, bits); */ *bcount = speed*timelim*((bits+7)/8L); /* fprintf(stderr,"bc=%ld\n",bcount); */ if (stereo) *bcount <<= 1; /* fprintf(stderr,"bc2=%ld\n",bcount); */ } if(samplim) { *bcount = samplim*((bits+7)/8L); if (stereo) *bcount <<= 1; } if(timejmp) { *bjump = speed*timejmp*((bits+7)/8L); if (stereo) *bjump <<= 1; } if(sampjmp) { *bjump = sampjmp*((bits+7)/8L); 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,0, hd_buf, 1, speed, bits, stereo); } void playwav(int thefd, char hd_buf[20], int mods, int speed, int bits, int stereo, long timelim, long samplim, long timejmp, long sampjmp ) { wavhead wavhd; int count; long bcount = 0, bjump = 0; memcpy((void*)&wavhd, (void*)hd_buf, 20); count = read(thefd, ((char*)&wavhd)+20, sizeof(wavhd) - 20); if(wavhd.format != 1) Die("Input is not a PCM WAV file"); #ifndef LP2CD if (! (mods&MSPEED)) /* This should _not_ be overridden by the commandline options, for those are always 44100 Hz 16 bit Stereo, and we want _all_ wavs to play correctly. */ #endif speed = wavhd.sample_fq; #ifndef LP2CD if (! (mods&MBITS)) #endif bits = wavhd.bit_p_spl; #ifndef LP2CD if (! (mods&MSTEREO)) #endif stereo = wavhd.modus - 1; #ifndef LP2CD fprintf(stderr, "Playing WAVE : %d bit, Speed %d %s ...\n", bits, speed, (stereo)? "Stereo" : "Mono"); #endif /* By CS: */ getbcount(speed, bits, stereo, &bcount, timelim, samplim, timejmp, sampjmp, &bjump); if (bjump) lseek(thefd, bjump, SEEK_CUR); diskread(thefd, bcount, bjump, 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) { #ifdef DEBUG fprintf(stderr, "Terminating\n"); #endif diskread(thefd, -1,0, NULL, 1, speed, bits, stereo); quit = 1; continue; } #ifdef DEBUG fprintf(stderr, "Block of type %d found\n", ccblock.BlockID); #endif 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,0, 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,0, NULL, 0, speed, bits, stereo); lastblocktype = 9; } break; case 0: #ifdef DEBUG fprintf(stderr, "Terminating\n"); #endif diskread(thefd, -1,0, NULL, 1, speed, bits, stereo); quit = 1; break; case 6: inloop = 1; read(thefd, (char*)&loop_times, 2); loop_times++; #ifdef DEBUG fprintf(stderr, "Beginning loop %d\n", loop_times); #endif 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) { #ifdef DEBUG fprintf(stderr, "Looping...\n"); #endif 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; } } } /* ----- BELOW: ADDED BY CS ------------------------------------------------ */ void init_curses(void) { initscr(); keypad(stdscr, TRUE); nonl(); cbreak(); noecho(); refresh(); /* Xterms erase everything after the first refresh, so refresh one time before anything is added. */ return; } void finish_curses(int sig) { endwin(); exit(0); } gramofile-1.6/bplaysrc/bplay.lsm100644 1751 144 1236 6277703170 15717 0ustar costarusersBegin3 Title: bplay Version: 0.96 Entered-date: Sun Feb 9, 1997 Description: A buffered audio play/record program. Handles WAV, VOC and raw sound files. Should allow glith-free recording at 44100kHz/16 bit stereo on most hardware. Keywords: audio play record Author: davidm@cs.usyd.edu.au (David Monro) Maintained-by: davidm@cs.usyd.edu.au (David Monro) Primary-site: milawa.psrg.cs.usyd.edu.au /pub/davidm 18kB bplay-0.96.tar.gz 1kB bplay.lsm Alternate-site: sunsite.unc.edu /pub/linux/apps/sound/players 18kB bplay-0.96.tar.gz 1kB bplay.lsm Platform: Linux and supported sound card Copying-policy: GPL End gramofile-1.6/bplaysrc/brec.1100644 1751 144 21 6130214300 14773 0ustar costarusers.so man1/bplay.1 gramofile-1.6/bplaysrc/fmtheaders.h100640 1751 144 3523 6277702724 16367 0ustar costarusers#ifndef _FMTHEADERS_H #define _FMTHEADERS_H 1 #include /* Definitions for .VOC files */ #define VOC_MAGIC "Creative Voice File\032" #define DATALEN(bp) ((u_long)(bp.BlockLen[0]) | \ ((u_long)(bp.BlockLen[1]) << 8) | \ ((u_long)(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_long main_chunk; /* 'RIFF' */ u_long length; /* Length of rest of file */ u_long chunk_type; /* 'WAVE' */ u_long sub_chunk; /* 'fmt ' */ u_long 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_long sample_fq; /* frequence of sample */ u_long byte_p_sec; u_short byte_p_spl; /* samplesize; 1 or 2 bytes */ u_short bit_p_spl; /* 8, 12 or 16 bit */ u_long data_chunk; /* 'data' */ u_long data_length; /* samplecount (lenth of rest of block?)*/ } wavhead; #endif gramofile-1.6/bplaysrc/shmbuf.c100644 1751 144 37701 7067752360 15554 0ustar costarusers/* ** ** bplay/shmbuf.c (C) David Monro 1996 ** ** Copyright under the GPL - see the file COPYING in this directory ** ** Adapted by J.A. Bezemer for use with GramoFile - July, August 1998 ** ** Patch to compile with egcs from Daniel Kobras, applied by J.A. Bezemer ** - October, 1998 */ #include #include #include #include #include #include #include #include #include #include #include "../errorwindow.h" #include "../clrscr.h" #include "../playwav.h" #include "../secshms.h" #include "../reclp_main.h" #include "../boxes.h" #include "../buttons.h" #include #ifndef OLD_CURSES #include #else #include #endif #ifndef SEMMSL #ifdef __FreeBSD__ /* * you may want to adjust this to whats configured into your kernel, * 30 is just the current default. (see ) -nox */ #define SEMMSL 30 #else #define SEMMSL 32 #endif #endif #ifdef _SEM_SEMUN_UNDEFINED union semun /* This has gone out of standard */ { /* libc headers as of glibc2.1, */ int val; /* we need to define it ourselves.*/ struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; }; #endif extern void finish_curses(int sig); /* The size of the big array */ /* (currently 256K - nearly 1.5 sec at max rate) */ #define BIGBUFFSIZE 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; /* 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 prototypes */ extern void ErrDie(char *err); extern void snd_parm(int speed, int bits, int stereo); extern void sync_audio(void); void init_shm(void) { int i; /* 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; totalrd += count; } #ifdef VUMETER maxleft = 0; maxright = 0; trgt = buffinf[cbuff].count; for (numwr = 0; numwr < trgt; numwr += 4 ) { leftvalue=abs(*ssptr); if (leftvalue > maxleft) maxleft = leftvalue; ssptr ++; rightvalue=abs(*ssptr); if (rightvalue > maxright) maxright = rightvalue; ssptr ++; if (leftvalue > 16383 || rightvalue > 16383) samplesabove50pct ++; if (leftvalue > 29490 || rightvalue > 29490) samplesabove90pct ++; if (leftvalue > 32439 || rightvalue > 32439) samplesabove99pct ++; if (leftvalue > 32764 || rightvalue > 32764) samplestooloud ++; } move(ERROR_WINDOW_Y + 2,ERROR_WINDOW_X +1); addstr("L: ="); leftvalue = maxleft / (32768/(ERROR_WINDOW_W-6)); for (numwr = 0; numwr < leftvalue; numwr ++) addch(numwr >= ERROR_WINDOW_W-8 ? '#' : '='); for (; numwr < ERROR_WINDOW_W-6; numwr ++) addch(' '); move(ERROR_WINDOW_Y + 3,ERROR_WINDOW_X +1); addstr("R: ="); rightvalue = maxright / (32768/(ERROR_WINDOW_W-6)); for (numwr = 0; numwr < rightvalue; numwr ++) addch(numwr >= ERROR_WINDOW_W-8 ? '#' : '='); for (; numwr < ERROR_WINDOW_W-6; numwr ++) addch(' '); move(0,79); refresh(); #endif /* 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); #ifdef VUMETER /* Display some informative data. This is really weird: we display it here (in the child), then exit() and wait for a key in the _parent_ process. But it's the only simple way to get it working */ clearscreen(RECLP_HEADERTEXT); printw("\n\n"); printw(" Recording information:\n\n\n"); fsec2hmsf ( (double) totalrd / (4 * 44100) , timestring); printw(" Recorded time : %s\n", timestring); printw(" Recorded samples : %11ld\n", totalrd / 4); printw(" Recorded bytes : %11ld (excl. header)\n", totalrd); printw("\n"); printw(" Samples above 50%% of max. volume : %9ld (%5.1f%%)\n", samplesabove50pct, samplesabove50pct * 100. / (totalrd/4)); printw(" Samples above 90%% of max. volume : %9ld (%5.1f%%)\n", samplesabove90pct, samplesabove90pct * 100. / (totalrd/4)); printw(" Samples above 99%% of max. volume : %9ld (%5.1f%%)\n", samplesabove99pct, samplesabove99pct * 100. / (totalrd/4)); printw(" Really too loud (clipped) samples : %9ld (%5.1f%%)\n", samplestooloud, samplestooloud * 100. / (totalrd/4)); #if 0 /* The computation of the avg volume is not simple. One approach is totalvolume+=abs(sampleleft)+abs(sampleright) for each sample, but if totalvolume gets too big, nothing is added any more (lack of precision). If anyone has a better (working) idea, please tell me! */ printw("\n"); printw(" Average volume : %7.1f (%5.1f%% of max.)\n", totalvolume / (totalrd/2), (totalvolume / (totalrd/2) * 100) / 32768); /* (totalrd/2)=((totalrd/4)*2) */ #endif /* 0 */ ok_button.text = " OK "; ok_button.y = 20; ok_button.x = 71; ok_button.selected = TRUE; button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); move (0, 79); refresh(); #endif exit(0); } else { /* Parent reads audio */ int cbuff = 0; long totalrd = 0; int stoprecording=0; int in_ch; while (totalrd < totalcount && !stoprecording) { long 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; /* Mark the buffer dirty */ up(disksemid, cbuff); /* Update the amount done */ totalrd += numrd; /* Tell the reader to stop if needed */ in_ch=getch(); #ifdef DEBUG printw(" %d",cbuff); #endif if (in_ch==KEY_ENTER || in_ch==13 || in_ch==27) stoprecording=1; if ( ((totalrd >= totalcount) && terminate) || stoprecording) buffinf[cbuff].last = 1; /* Update the counter */ cbuff++; cbuff%=numbuffs; } /* Tidy up and wait for the child */ close(audio); /* XXX fix the occasional deadlock in the following wait() -nox */ for (i = 0; i < numbuffs; i++) up(disksemid, i); wait(NULL); /* Free all the semaphores */ cleanupsems(); #ifdef VUMETER nodelay(stdscr, TRUE); /* child has displayed informative data */ do i = getch (); while (i != 13 && i != KEY_ENTER && i != 27); #endif } } void diskread(int infd, long totalplay, long skipped, char hd_buf[20], int terminate, int speed, int bits, int stereo) { int count, i, limited = 0; char *tmppt; long numread, totalread = 0; int first = 1; static int triggered = 0; /* Have we let the writer go? */ static int cbuff = 0; /* Which buffer */ char tempstring[50]; int in_ch; if (totalplay) limited = 1; if (totalplay == -1) { totalplay = 0; limited = 1; } clearscreen(PLAYWAV_HEADERTEXT); error_window_display("Playing...", " Stop "); nodelay(stdscr, TRUE); refresh(); 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; } #ifdef DEBUG fprintf(stderr, "in:%d, %d\n", cbuff, numread); #endif /* Update the count for this block */ buffinf[cbuff].count = numread; totalread += numread; in_ch = getch(); /* Was it our last block? */ if (numread < abuf_size ) { break; } if ( in_ch == 27 || in_ch == KEY_ENTER || in_ch == 13 ) { mvprintw(ERROR_WINDOW_Y+2, ERROR_WINDOW_X+1, "Time: "); move(0,79); refresh(); break; } if(triggered) { up(sndsemid, cbuff); fsec2hmsf( (skipped + totalread - BIGBUFFSIZE - 65536.) / (speed * (bits/8) * (stereo+1)), tempstring); mvprintw(ERROR_WINDOW_Y+2, ERROR_WINDOW_X+1, "Time: %s", tempstring); move(0,79); refresh(); /* fprintf(stderr,"\nbyte(1): %ld", skipped + totalread - BIGBUFFSIZE - 65536); */ } else if(cbuff == numbuffs-1) { #ifdef DEBUG fprintf(stderr, "Triggering (in loop)\n"); #endif 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) #ifdef DEBUG fprintf(stderr, "Triggering (after loop, partial)\n"); #endif /* 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)) { #ifdef DEBUG fprintf(stderr, "Triggering (after loop, full)\n"); #endif 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; } #ifdef DEBUG fprintf(stderr, "out:%d, %d\n", cbuff, numwr); #endif /* 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; for (i=0;i #include #include #include #include #include #ifndef __FreeBSD__ #include #else #include #endif #define AUDIO "/dev/dsp" /* Globals */ int audio, abuf_size, fmt_mask; /* Prototypes */ void sync_audio(void); /* Extern globals */ extern char *progname; /* 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(AUDIO, (recorder)? O_RDONLY : O_WRONLY); if (audio == -1) ErrDie(AUDIO); #if 0 if (ioctl(audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size) < 0) ErrDie(AUDIO); if (abuf_size < 4096 || abuf_size > 65536) Die("invalid audio buffer size"); fprintf(stderr, "abuf_size = %d\n", abuf_size); #else abuf_size = 65536; #endif #if 1 if (ioctl(audio, SNDCTL_DSP_GETFMTS, &fmt_mask) < 0) ErrDie(AUDIO); #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(AUDIO); if (ioctl(audio, SNDCTL_DSP_STEREO, &stereo) < 0) ErrDie(AUDIO); if (ioctl(audio, SNDCTL_DSP_SPEED, &speed) < 0) ErrDie(AUDIO); } oldspeed = speed; oldbits = bits; oldstereo = stereo; } void sync_audio(void) { if (ioctl (audio, SNDCTL_DSP_SYNC, NULL) < 0) ErrDie(AUDIO); } gramofile-1.6/buttons.c100644 1751 144 1030 7070217415 14100 0ustar costarusers/* Buttons * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "buttons.h" #ifndef OLD_CURSES #include #else #include #endif void button_display (button_t * button) { int y, x; if (button->selected) attron (A_STANDOUT); mvaddstr (button->y, button->x, button->text); getyx (stdscr, y, x); move (y, x - 1); if (button->selected) attroff (A_STANDOUT); } gramofile-1.6/buttons.h100644 1751 144 637 7070217416 14102 0ustar costarusers/* Buttons - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_BUTTONS_H #define HAVE_BUTTONS_H typedef struct { char *text; int y; int x; int selected; } button_t; extern void button_display (button_t * button); #endif /* HAVE_BUTTONS_H */ gramofile-1.6/checkfile.c100644 1751 144 3453 7070217415 14332 0ustar costarusers/* Check file * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "checkfile.h" #include #include /* ---- From GNU shellutils, lib/stripslash.h: */ /* Remove trailing slashes from PATH. This is useful when using filename completion from a shell that adds a "/" after directory names (such as tcsh and bash), because the Unix rename and rmdir system calls return an "Invalid argument" error when given a path that ends in "/" (except for the root directory). */ void strip_trailing_slashes (char *path) { int last; last = strlen (path) - 1; while (last > 0 && path[last] == '/') path[last--] = '\0'; } /* ---- End */ int checkfile (char *filename) { struct stat buf; int i; char myfilename[250]; char *slash; strip_trailing_slashes (filename); i = stat (filename, &buf); if (!i) /* file or dir found */ { if (S_ISDIR (buf.st_mode)) { if (strcmp (filename, "/")) strcat (filename, "/"); return DIR_EXISTS; } else return FILE_EXISTS; } else /* file does not exist, check dir */ { strcpy (myfilename, filename); /* ---- Adapted from GNU shellutils, src/dirname.c: */ slash = strrchr (myfilename, '/'); if (slash == NULL) strcpy (myfilename, "."); else { /* Remove any trailing slashes and final element. */ while (slash > myfilename && *slash == '/') --slash; slash[1] = 0; } /* ---- End */ i = stat (myfilename, &buf); if (!i) /* dir found */ { if (S_ISDIR (buf.st_mode)) return DIR_OK_NEW_FILE; return DIR_WRONG; } else /* dir does not exist */ return DIR_WRONG; } /* else: file not found */ } gramofile-1.6/checkfile.h100644 1751 144 1434 7070217416 14335 0ustar costarusers/* Check file - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_CHECKFILE_H #define HAVE_CHECKFILE_H #define FILE_EXISTS 1 /* The string contains the name of an existing file */ #define DIR_EXISTS 2 /* The string contains the name of an existing directory */ #define DIR_OK_NEW_FILE 3 /* The string contains the name of a new file, in an already existing directory */ #define DIR_WRONG 4 /* The string contains the name of a file with a non-existing directory-path */ extern void strip_trailing_slashes (char *path); extern int checkfile (char *filename); #endif /* HAVE_CHECKFILE_H */ gramofile-1.6/clrscr.c100644 1751 144 1130 7070217415 13673 0ustar costarusers/* Clear Screen + Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "clrscr.h" #ifndef OLD_CURSES #include #else #include #endif void header (char *headertext) { int i; mvaddstr (0, 1, PROGRAM_NAME); mvaddstr (0, (80 - strlen (headertext) + 1) / 2, headertext); move (1, 0); for (i = 0; i < 80; i++) addch (ACS_S1); } void clearscreen (char *headertext) { clear (); refresh (); header (headertext); } gramofile-1.6/clrscr.h100644 1751 144 615 7070217416 13670 0ustar costarusers/* Clear Screen + Header - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_CLRSRC_H #define HAVE_CLRSCR_H #define PROGRAM_NAME "GramoFile 1.6" void header (char *headertext); void clearscreen (char *headertext); #endif /* HAVE_CLRSCR_H */ gramofile-1.6/dirfilemenu.c100644 1751 144 6055 7070217415 14721 0ustar costarusers/* Dir/file menus * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "dirfilemenu.h" #include "scrollmenu.h" #include #include #include #include void dirfilemenu (char *basedir, scrollmenu_t * menu) /* NOTE: basedir must contain last '/', like "/home/" */ { struct dirent **namelist; int n; char mybasedir[250]; char helpstring[250]; char *firstslash; int i, j, k; int indent = 0; int isadir; /* 0 if no dir, 1 if dir. */ struct stat filestats; n = scandir (basedir, &namelist, NULL, alphasort); if (n < 0) menu->number = 0; else { menu->items = (char **) malloc ((n + 100) * sizeof (char *)); for (i = 0; i < n + 100; i++) menu->items[i] = NULL; strcpy (mybasedir, basedir); i = 0; do { firstslash = strchr (mybasedir, '/'); if (firstslash != NULL) { menu->items[i] = (char *) malloc ((firstslash - mybasedir + 2 + indent) * sizeof (char)); for (k = 0; k < indent; k++) menu->items[i][k] = ' '; strncpy (menu->items[i] + indent, mybasedir, firstslash - mybasedir + 1); menu->items[i][firstslash - mybasedir + 1 + indent] = '\0'; strcpy (helpstring, firstslash + 1); strcpy (mybasedir, helpstring); i++; indent++; } } while (strlen (mybasedir) > 0); menu->last_of_1st_part = i - 1; for (j = 0; j < n; j++) { if (strcmp (namelist[j]->d_name, ".") && strcmp (namelist[j]->d_name, "..")) { strcpy (helpstring, basedir); strcat (helpstring, namelist[j]->d_name); stat (helpstring, &filestats); if (S_ISDIR (filestats.st_mode)) isadir = 1; else isadir = 0; menu->items[i] = (char *) malloc ( (strlen (namelist[j]->d_name) + 1 + indent + isadir) * sizeof (char)); for (k = 0; k < indent; k++) menu->items[i][k] = ' '; strcpy (menu->items[i] + indent, namelist[j]->d_name); if (isadir) strcat (menu->items[i], "/"); i++; } } menu->number = i; } } int dirfilemenu_process_select (scrollmenu_t * menu, char *dirfile) /* Returns: 0 if file, then complete filename in dirfile 1 if dir, then new complete dirname in dirfile */ { int i; if (menu->selected <= menu->last_of_1st_part) /* parent dir */ { dirfile[0] = '\0'; for (i = 0; i <= menu->selected; i++) strcat (dirfile, menu->items[i] + i); menu->selected++; return 1; } else /* this dir */ { dirfile[0] = '\0'; for (i = 0; i <= menu->last_of_1st_part; i++) strcat (dirfile, menu->items[i] + i); strcat (dirfile, menu->items[menu->selected] + menu->last_of_1st_part + 1); if (menu->items[menu->selected] [strlen (menu->items[menu->selected]) - 1] == '/') { /* subdir */ menu->selected = menu->last_of_1st_part + 2; return 1; } else /* file */ return 0; } } gramofile-1.6/dirfilemenu.h100644 1751 144 1171 7070217416 14721 0ustar costarusers/* Dir/file menus - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_DIRFILEMENU_H #define HAVE_DIRFILEMENU_H #include "scrollmenu.h" extern void dirfilemenu (char *basedir, scrollmenu_t * menu); /* NOTE: basedir must contain last '/', like "/home/" */ extern int dirfilemenu_process_select (scrollmenu_t * menu, char *dirfile); /* Returns: 0 if file, then complete filename in dirfile 1 if dir, then new complete dirname in dirfile */ #endif /* HAVE_DIRFILEMENU_H */ gramofile-1.6/errorwindow.c100644 1751 144 2226 7070217415 14773 0ustar costarusers/* Error window * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "errorwindow.h" #include "buttons.h" #include "boxes.h" #include "textwindow.h" #ifndef OLD_CURSES #include #else #include #endif void error_window_display (char *text, char *buttontext) { button_t ok_button; ok_button.text = buttontext; ok_button.y = ERROR_WINDOW_Y + ERROR_WINDOW_H - 1; ok_button.x = ERROR_WINDOW_X + ERROR_WINDOW_W - 1 - strlen (ok_button.text); ok_button.selected = TRUE; mybox (ERROR_WINDOW_Y - 1, ERROR_WINDOW_X - 1, ERROR_WINDOW_H + 2, ERROR_WINDOW_W + 2); display_textwin ("", ERROR_WINDOW_Y, ERROR_WINDOW_X, ERROR_WINDOW_H, ERROR_WINDOW_W); display_textwin (text, ERROR_WINDOW_Y, ERROR_WINDOW_X + 1, ERROR_WINDOW_H, ERROR_WINDOW_W - 2); button_display (&ok_button); move (0, 79); refresh (); } void error_window (char *text) { int i; error_window_display (text, " OK "); do i = getch (); while (i != 13 && i != KEY_ENTER && i != 27); clear (); refresh (); } gramofile-1.6/errorwindow.h100644 1751 144 766 7070217416 14770 0ustar costarusers/* Error window - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_ERRORWINDOW_H #define HAVE_ERRORWINDOW_H #define ERROR_WINDOW_Y 9 #define ERROR_WINDOW_X 20 #define ERROR_WINDOW_H 5 #define ERROR_WINDOW_W 40 extern void error_window_display (char *text, char *buttontext); extern void error_window (char *text); #endif /* HAVE_ERRORWINDOW_H */ gramofile-1.6/fmtheaders.h100644 1751 144 3643 7070217416 14546 0ustar costarusers#ifndef _FMTHEADERS_H #define _FMTHEADERS_H 1 #include /* Definitions for .VOC files */ #define VOC_MAGIC "Creative Voice File\032" #define DATALEN(bp) ((u_long)(bp.BlockLen[0]) | \ ((u_long)(bp.BlockLen[1]) << 8) | \ ((u_long)(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_long main_chunk; /* 'RIFF' */ u_long length; /* Length of rest of file */ u_long chunk_type; /* 'WAVE' */ u_long sub_chunk; /* 'fmt ' */ u_long 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_long sample_fq; /* frequence of sample */ u_long byte_p_sec; u_short byte_p_spl; /* samplesize; 1 or 2 bytes */ u_short bit_p_spl; /* 8, 12 or 16 bit */ u_long data_chunk; /* 'data' */ u_long data_length; /* samplecount (lenth of rest of block?) */ } wavhead; #endif gramofile-1.6/helpline.c100644 1751 144 664 7070217415 14176 0ustar costarusers/* Helpline (footer) * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "helpline.h" #ifndef OLD_CURSES #include #else #include #endif void helpline (char *helptext) { int i; move (22, 0); for (i = 0; i < 80; i++) addch (ACS_S9); mvaddstr (23, 0, helptext); } gramofile-1.6/helpline.h100644 1751 144 502 7070217416 14173 0ustar costarusers/* Helpline (footer) - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_HELPLINE_H #define HAVE_HELPLINE_H void helpline (char *helptext); #endif /* HAVE_HELPLINE_H */ gramofile-1.6/mainmenu.c100644 1751 144 13017 7070217415 14243 0ustar costarusers/* Main menu * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ /* Mouse support here only? Kinda confusing... So don't support mouse. */ #define DONT_USE_MOUSE #include "mainmenu.h" #include "reclp_main.h" #include "textwindow.h" #include "signpr_main.h" #include "clrscr.h" #include "helpline.h" #include "errorwindow.h" #include "playwav.h" #include "tracksplit.h" #include #include #ifndef OLD_CURSES #include #else #include #endif void displaymenu_onscreen (char **options, char **helptext, int number, int selected, int y, int x, int distance) { /* (y,x): upper left position */ int i; for (i = 0; i < number; i++) { mvprintw (y + i * distance, x, "%d.", i + 1); if (i == selected) attron (A_STANDOUT); mvaddstr (y + i * distance, x + 3, options[i]); if (i == selected) attroff (A_STANDOUT); } mvaddstr (16, 1, options[selected]); display_textwin (helptext[selected], 17, 2, 5, 76); move (0, 79); refresh (); } int displaymenu (char **options, char **helptext, int number, int preselected) /* Returns: -1 if Escape pressed, >=0 Enter or 1..9 */ { int i; int selected; int exitfunc = 0; int in_ch; #ifdef NCURSES_MOUSE_VERSION MEVENT mouse_event; #endif #ifdef NCURSES_MOUSE_VERSION #ifndef DONT_USE_MOUSE mousemask (ALL_MOUSE_EVENTS, NULL); #endif #endif selected = preselected; do { displaymenu_onscreen (options, helptext, number, selected, DISPLAYMENU_Y, DISPLAYMENU_X, DISPLAYMENU_DISTANCE); in_ch = getch (); #ifdef NCURSES_MOUSE_VERSION if (in_ch == KEY_MOUSE) { getmouse (&mouse_event); if (mouse_event.bstate == BUTTON1_CLICKED || mouse_event.bstate == BUTTON1_DOUBLE_CLICKED) { for (i = 0; i < number; i++) if (mouse_event.y == DISPLAYMENU_Y + i * DISPLAYMENU_DISTANCE && mouse_event.x >= DISPLAYMENU_X && mouse_event.x < DISPLAYMENU_X + 3 + strlen (options[i]) ) { displaymenu_onscreen (options, helptext, number, i, DISPLAYMENU_Y, DISPLAYMENU_X, DISPLAYMENU_DISTANCE); usleep (100000); selected = i; exitfunc = TRUE; } } } #endif /* NCURSES_MOUSE_VERSION */ switch (in_ch) { case 'k': /* Support some `less' keys. But only here... */ case 'K': case KEY_UP: selected--; break; case 'j': case 'J': case KEY_DOWN: selected++; break; case 9: /* TAB */ selected++; if (selected >= number) selected = 0; break; case 13: /* Enter/Return */ case KEY_ENTER: exitfunc = TRUE; break; } if (in_ch >= '1' && in_ch <= '9') { selected = in_ch - '1'; if (selected < number) { displaymenu_onscreen (options, helptext, number, selected, DISPLAYMENU_Y, DISPLAYMENU_X, DISPLAYMENU_DISTANCE); usleep (100000); exitfunc = TRUE; } } if (selected < 0) selected = 0; if (selected >= number) selected = number - 1; if (in_ch == 27 /* Escape */ || in_ch == 'q' || in_ch == 'Q' || in_ch == '0') { selected = -1; exitfunc = TRUE; } } while (!exitfunc); #ifdef NCURSES_MOUSE_VERSION mousemask (0, NULL); #endif return selected; } void mainmenu (char *startdir) { int selected = 0; #define MAINMENU_OPTIONS 6 char *menu_options[MAINMENU_OPTIONS] = { "Record audio to a sound file ", "[Copy sound from an audio CD to a file]", "Locate tracks ", "Process the audio signal ", "[Write an audio CD] ", "Play a sound file "}; char *menu_helptext[MAINMENU_OPTIONS] = { "With this option, audio from various sources (like gramophone records) \ can be recorded (sampled). The digital audio data is stored in a sound file \ (.wav format) on the harddisk.", "This option is not implemented yet. You can use the `cdda2wav' \ or `cdparanoia' program to copy digital audio from a CD to a sound file \ (.wav format) on the harddisk.", "The starts and ends of tracks in a large sound file can be \ detected automatically with this option. The `process signal' \ option uses the resulting .tracks file to actually split tracks.", "With this option, the digital audio from a sound file on the harddisk \ can be processed. For example, ticks may be filtered out. If track \ separation points are computed (previous option), separate audio files \ will be generated, each containing one track.", "This option is not implemented yet. You can use the `cdrecord' \ or `xcdroast' program to create CDs from sound files on the harddisk.", "(Parts of) sound files can be played with this option." }; do { clearscreen ("Main Menu"); helpline ( " Arrows/TAB: Navigate Enter: Select option 0/Q/Escape: Exit "); selected = displaymenu (menu_options, menu_helptext, MAINMENU_OPTIONS, selected); switch (selected) { case 0: record_from_lp (startdir); break; case 1: error_window ("This option has not yet been implemented."); break; case 2: tracksplit_main (startdir); break; case 3: signproc_main (startdir); break; case 4: error_window ("This option has not yet been implemented."); break; case 5: playwav_main (startdir); break; } } while (selected != -1); } gramofile-1.6/mainmenu.h100644 1751 144 1203 7070217416 14223 0ustar costarusers/* Main menu - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_MAINMENU_H #define HAVE_MAINMENU_H #define DISPLAYMENU_Y 3 #define DISPLAYMENU_X 5 #define DISPLAYMENU_DISTANCE 2 void displaymenu_onscreen (char **options, char **helptext, int number, int selected, int y, int x, int distance); /* (y,x): upper left position */ int displaymenu (char **options, char **helptext, int number, int preselected); void mainmenu (char *startdir); #endif /* HAVE_MAINMENU_H */ gramofile-1.6/playwav.c100644 1751 144 45124 7070217415 14121 0ustar costarusers/* Playing a sound file * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "playwav.h" #include "scrollmenu.h" #include "stringinput.h" #include "buttons.h" #include "boxes.h" #include "dirfilemenu.h" #include "errorwindow.h" #include "textwindow.h" #include "checkfile.h" #include "yesnowindow.h" #include "helpline.h" #include "clrscr.h" #include "secshms.h" #include "fmtheaders.h" #include "signpr_main.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif void playwav_playit (char *filename, int usebeginendtime, double begintime, double endtime) { char shellcmd[500]; switch (checkfile (filename)) { case FILE_EXISTS: if (usebeginendtime) sprintf (shellcmd, "bplay_gramo -S -s 44100 -b 16 -J %ld -T %ld %s", (long) (begintime * 44100), (long) ((endtime - begintime) * 44100), filename); else sprintf (shellcmd, "bplay_gramo -S -s 44100 -b 16 %s", filename); /* defaults for raw files (but no -r, so .wav's supply their own parameters) - you can even listen to executables in CD quality (: */ def_prog_mode (); /* save terminal state */ system (shellcmd); reset_prog_mode (); /* reset terminal state */ clear (); break; case DIR_EXISTS: error_window ("The specified name is of a directory. A \ file name must be specified."); break; case DIR_OK_NEW_FILE: case DIR_WRONG: error_window ("The specified file does not exist."); break; default: error_window ("Fell out of switch, playwav #2"); break; } } #define PLAY_TRACK 1 #define PLAY_BEGINNING 2 #define PLAY_END 3 #define PLAY_BEFORE 4 #define PLAY_AFTER 5 /* These may be fractional, like 5.2, if you like that better. */ #define BEGINNING_SECS 5 #define END_SECS 4 #define BEFORE_SECS 3 #define AFTER_SECS 3 void playwav_track (char *filename, int track, int action) { beginendsample_t tracktimes[100]; int number_of_tracks; double begintime, endtime; char helpstring[200]; if (!load_track_times (filename, tracktimes, &number_of_tracks)) return; if (track > number_of_tracks || track < 1) { sprintf (helpstring, "There are only %d tracks.", number_of_tracks); error_window (helpstring); return; } switch (action) { case PLAY_TRACK: begintime = tracktimes[track].begin; endtime = tracktimes[track].end; /* use samples here, convert to seconds after switch */ break; case PLAY_BEGINNING: begintime = tracktimes[track].begin; endtime = tracktimes[track].begin + BEGINNING_SECS * 44100; break; case PLAY_END: begintime = tracktimes[track].end - END_SECS * 44100; endtime = tracktimes[track].end; break; case PLAY_BEFORE: begintime = tracktimes[track].begin - BEFORE_SECS * 44100; endtime = tracktimes[track].begin; break; case PLAY_AFTER: begintime = tracktimes[track].end; endtime = tracktimes[track].end + AFTER_SECS * 44100; break; default: error_window ("Fell out of switch, playwav #3"); return; } if (begintime < 0) begintime = 0; if (endtime < 0) endtime = 0; if (begintime >= endtime) return; /* samples -> seconds here */ playwav_playit (filename, 1, begintime / 44100, endtime / 44100); } int playwav_select_file (char *startdir, char *selectedfile, int *usebeginendtime, double *begintime, double *endtime) /* Returns 0: canceled, 1: OK */ { scrollmenu_t dirfilelist; stringinput_t string; button_t ok_button, cancel_button; int dont_stop = TRUE; int returnval = 0; int focus; int in_ch; int i; char helpstring[500]; char *charpointer; struct stat filestats; int oldselected; double tempdouble = 0; long templong; char tempstring[250]; stringinput_t trackstring, begintimestring, endtimestring; button_t beginend_check; struct stat buf; char *helplines[8] = { " Select name of sound file to be played. TAB: Next field", " Enter: Play track B/E: Beginning/End F/A: beFore/After +/-: Prev/Next", " Play only a part of the sound file. TAB: Next field", " Enter begin time of part to be played. TAB: Next field", " Enter end time of part to be played. TAB: Next field", " Enter name of sound file to be played. TAB: Next field", " Back to main menu. TAB: Next field", " Play the specified (part of the) sound file. TAB: Next field"}; dirfilelist.y = 3; dirfilelist.x = 5; dirfilelist.h = 12; dirfilelist.w = 32; dirfilelist.firstonscreen = 0; dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = dirfilelist.last_of_1st_part + 1; string.maxlen = 500; string.string = (char *) malloc (string.maxlen * sizeof (char)); if (selectedfile[0] == '\0') strcpy (string.string, startdir); else strcpy (string.string, selectedfile); string.y = 17; string.x = 5; string.w = 70; string.cursorpos = strlen (string.string); string.firstcharonscreen = strlen (string.string) - string.w + 2; if (string.firstcharonscreen < 0) string.firstcharonscreen = 0; ok_button.text = " Play "; ok_button.y = 20; ok_button.x = 69; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; trackstring.maxlen = 500; trackstring.string = (char *) malloc ( trackstring.maxlen * sizeof (char)); strcpy (trackstring.string, "1"); trackstring.y = 11; trackstring.x = 54; trackstring.w = 10; trackstring.cursorpos = strlen (trackstring.string); trackstring.firstcharonscreen = 0; begintimestring.maxlen = 500; begintimestring.string = (char *) malloc ( begintimestring.maxlen * sizeof (char)); fsec2hmsf (*begintime, begintimestring.string); begintimestring.y = 14; begintimestring.x = 59; begintimestring.w = 18; begintimestring.cursorpos = strlen (begintimestring.string); begintimestring.firstcharonscreen = 0; endtimestring.maxlen = 500; endtimestring.string = (char *) malloc ( endtimestring.maxlen * sizeof (char)); fsec2hmsf (*endtime, endtimestring.string); endtimestring.y = 15; endtimestring.x = 59; endtimestring.w = 18; endtimestring.cursorpos = strlen (endtimestring.string); endtimestring.firstcharonscreen = 0; beginend_check.text = ""; /* see below */ beginend_check.y = 13; beginend_check.x = 42; beginend_check.selected = FALSE; clearscreen (PLAYWAV_HEADERTEXT); if (selectedfile[0] == '\0') focus = 0; else focus = 5; do { if (*usebeginendtime) beginend_check.text = "[X] Use begin and end times"; else beginend_check.text = "[ ] Use begin and end times"; if (focus == 2) beginend_check.selected = TRUE; else beginend_check.selected = FALSE; if (focus == 6) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 7) ok_button.selected = TRUE; else ok_button.selected = FALSE; dirfilelist.hasfocus = (focus == 0); scrollmenu_display (&dirfilelist); mybox (dirfilelist.y - 1, dirfilelist.x - 1, dirfilelist.h + 2, dirfilelist.w + 2); mvprintw (dirfilelist.y - 1, dirfilelist.x + 1, "Files and directories:"); button_display (&beginend_check); stringinput_display (&trackstring); mvprintw (trackstring.y, trackstring.x - 7, "Track:"); stringinput_display (&begintimestring); mvprintw (begintimestring.y, begintimestring.x - 12, "Begin time:"); stringinput_display (&endtimestring); mvprintw (endtimestring.y, endtimestring.x - 12, "End time :"); stringinput_display (&string); mybox (string.y - 1, string.x - 1, 3, string.w + 2); mvprintw (string.y - 1, string.x + 1, "File name:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); header (PLAYWAV_HEADERTEXT); helpline (helplines[focus]); /* this really should be a switch... */ if (focus == 1) stringinput_display (&trackstring); else if (focus == 3) stringinput_display (&begintimestring); else if (focus == 4) stringinput_display (&endtimestring); else if (focus == 5) stringinput_display (&string); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* dirfilelist */ if (scrollmenu_stdkeys (in_ch, &dirfilelist) >= 0) { oldselected = dirfilelist.selected; i = dirfilemenu_process_select (&dirfilelist, helpstring); if (i == 0) /* filename in helpstring */ { strcpy (string.string, helpstring); focus = 7; string.cursorpos = strlen (string.string); string.firstcharonscreen = 0; if (!stat (helpstring, &buf)) { *begintime = 0; if (buf.st_size < sizeof (wavhead)) *endtime = 0; else *endtime = (buf.st_size - sizeof (wavhead)) / (2 * 2 * 44100.); fsec2hmsf (*begintime, begintimestring.string); begintimestring.cursorpos = strlen (begintimestring.string); begintimestring.firstcharonscreen = 0; fsec2hmsf (*endtime, endtimestring.string); endtimestring.cursorpos = strlen (endtimestring.string); endtimestring.firstcharonscreen = 0; } } else /* dir in helpstring */ { scrollmenu_delete_menu (&dirfilelist); dirfilemenu (helpstring, &dirfilelist); if (dirfilelist.number == 0) { error_window ( "No permission to read this directory."); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = oldselected; } else { strcpy (startdir, helpstring); dirfilelist.firstonscreen = 0; charpointer = strrchr (string.string, '/'); if (charpointer != NULL) strcat (helpstring, charpointer + 1); else strcat (helpstring, string.string); strcpy (string.string, helpstring); } } } else switch (in_ch) { case KEY_LEFT: focus--; break; case KEY_RIGHT: focus++; break; } break; case 1: /* tracks */ /* exclude keys like b,e,f,a,+,- */ if ((in_ch >= '0' && in_ch <= '9') || in_ch > 127) stringinput_stdkeys (in_ch, &trackstring); templong = atol (trackstring.string); switch (in_ch) { case KEY_ENTER: case 13: playwav_track (string.string, templong, PLAY_TRACK); break; case 'b': case 'B': playwav_track (string.string, templong, PLAY_BEGINNING); break; case 'e': case 'E': playwav_track (string.string, templong, PLAY_END); break; case 'f': case 'F': playwav_track (string.string, templong, PLAY_BEFORE); break; case 'a': case 'A': playwav_track (string.string, templong, PLAY_AFTER); break; case '+': case '=': if (templong < 99) templong++; sprintf (trackstring.string, "%ld", templong); trackstring.cursorpos = strlen (trackstring.string); trackstring.firstcharonscreen = 0; break; case '-': case '_': if (templong > 1) templong--; sprintf (trackstring.string, "%ld", templong); trackstring.cursorpos = strlen (trackstring.string); trackstring.firstcharonscreen = 0; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* Use begin/end times */ switch (in_ch) { case KEY_ENTER: case 13: case ' ': case 'x': case 'X': *usebeginendtime = 1 - *usebeginendtime; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 3: /* begin time */ stringinput_stdkeys (in_ch, &begintimestring); switch (in_ch) { case KEY_ENTER: case 13: strcpy (tempstring, begintimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); begintimestring.cursorpos = strlen (begintimestring.string); } else { *begintime = tempdouble; fsec2hmsf (tempdouble, begintimestring.string); begintimestring.cursorpos = strlen (begintimestring.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 4: /* end time */ stringinput_stdkeys (in_ch, &endtimestring); switch (in_ch) { case KEY_ENTER: case 13: strcpy (tempstring, endtimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); endtimestring.cursorpos = strlen (endtimestring.string); } else { *endtime = tempdouble; fsec2hmsf (tempdouble, endtimestring.string); endtimestring.cursorpos = strlen (endtimestring.string); focus = 5; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 5: /* string */ stringinput_stdkeys (in_ch, &string); if (in_ch == KEY_ENTER || in_ch == 13) { strcpy (helpstring, string.string); /* cut away last '/'-s */ while (strlen (helpstring) > 0 && helpstring[strlen (helpstring) - 1] == '/') helpstring[strlen (helpstring) - 1] = '\0'; strcat (helpstring, "/"); if (!stat (helpstring, &filestats) && S_ISDIR (filestats.st_mode)) { strcpy (startdir, helpstring); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.firstonscreen = 0; dirfilelist.selected = dirfilelist.last_of_1st_part + 1; strcpy (string.string, startdir); string.cursorpos = strlen (string.string); focus = 0; } else /* it's a file */ { focus = 7; if (!stat (string.string, &buf)) { *begintime = 0; if (buf.st_size < sizeof (wavhead)) *endtime = 0; else *endtime = (buf.st_size - sizeof (wavhead)) / (2 * 2 * 44100.); fsec2hmsf (*begintime, begintimestring.string); begintimestring.cursorpos = strlen (begintimestring.string); begintimestring.firstcharonscreen = 0; fsec2hmsf (*endtime, endtimestring.string); endtimestring.cursorpos = strlen (endtimestring.string); endtimestring.firstcharonscreen = 0; } } } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 6: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 7: /* OK */ if (in_ch == KEY_ENTER || in_ch == 13) { strcpy (tempstring, begintimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the begin time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); begintimestring.cursorpos = strlen (begintimestring.string); focus = 3; break; } *begintime = tempdouble; strcpy (tempstring, endtimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the end time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); endtimestring.cursorpos = strlen (endtimestring.string); focus = 4; break; } *endtime = tempdouble; if (*begintime > *endtime) { error_window ("The begin time is larger than the end time."); fsec2hmsf (*begintime, begintimestring.string); begintimestring.cursorpos = strlen (begintimestring.string); fsec2hmsf (*endtime, endtimestring.string); endtimestring.cursorpos = strlen (endtimestring.string); focus = 3; break; } switch (checkfile (string.string)) { case FILE_EXISTS: /* strcpy (selectedfile, string.string); returnval = 1; dont_stop = FALSE; */ playwav_playit (string.string, *usebeginendtime, *begintime, *endtime); break; case DIR_EXISTS: error_window ("The specified name is of a directory. A \ file name must be specified."); string.cursorpos = strlen (string.string); focus = 5; break; case DIR_OK_NEW_FILE: case DIR_WRONG: error_window ("The specified file does not exist."); string.cursorpos = strlen (string.string); focus = 5; break; default: error_window ("Fell out of switch, playwav #1"); break; } } /* if ENTER */ else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 7) focus = 0; if (focus < 0) focus = 7; } while (dont_stop); scrollmenu_delete_menu (&dirfilelist); free (string.string); return returnval; } void playwav_main (char *startdir) { char filename[250]; /* char shellcmd[500]; */ int usebeginendtime = 0; double begintime = 0, endtime = 0; filename[0] = '\0'; while (1) { if (!playwav_select_file (startdir, filename, &usebeginendtime, &begintime, &endtime)) return; #if 0 /* not used any more - integrated in select_file */ def_prog_mode (); /* save terminal state */ if (usebeginendtime) sprintf (shellcmd, "bplay_gramo -S -s 44100 -b 16 -J %ld -T %ld %s", (long) (begintime * 44100), (long) ((endtime - begintime) * 44100), filename); else sprintf (shellcmd, "bplay_gramo -S -s 44100 -b 16 %s", filename); /* defaults for raw files (but no -r, so .wav's supply their own parameters */ system (shellcmd); reset_prog_mode (); /* reset terminal state */ #endif /* 0 */ } } gramofile-1.6/playwav.h100644 1751 144 1033 7070217416 14076 0ustar costarusers/* Playing of a sound file - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_PLAYWAV_H #define HAVE_PLAYWAV_H #define PLAYWAV_HEADERTEXT "Play a sound file" int playwav_select_file (char *startdir, char *selectedfile, int *usebeginendtime, double *begintime, double *endtime); /* Returns 0: canceled, 1: OK */ void playwav_main (char *startdir); #endif /* HAVE_PLAYWAV_H */ gramofile-1.6/reclp_filenm.c100644 1751 144 20047 7070217415 15072 0ustar costarusers/* Record from LP - Get filename * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "reclp_filenm.h" #include "scrollmenu.h" #include "stringinput.h" #include "buttons.h" #include "boxes.h" #include "dirfilemenu.h" #include "errorwindow.h" #include "textwindow.h" #include "checkfile.h" #include "yesnowindow.h" #include "helpline.h" #include "clrscr.h" #include "reclp_main.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif int record_from_lp_get_filename (char *startdir, char *selectedfile) /* Returns 1 if file selected, 0 if canceled */ { scrollmenu_t dirfilelist; stringinput_t string; button_t ok_button, cancel_button, mixer_button; int dont_stop = TRUE; int returnval = 0; int focus = 1; int in_ch; int i; char helpstring[500]; char *charpointer; struct stat filestats; int oldselected; char *helplines[5] = { " Browse files and directories. TAB: Next field", " Enter name of destination sound file. TAB: Next field", " Back to main menu TAB: Next field", " Start `xmixer' to adjust recording volumes. TAB: Next field", " Start recording in the specified sound file. TAB: Next field"}; dirfilelist.y = 3; dirfilelist.x = 5; dirfilelist.h = 12; dirfilelist.w = 32; dirfilelist.firstonscreen = 0; dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = dirfilelist.last_of_1st_part + 1; string.maxlen = 500; string.string = (char *) malloc (string.maxlen * sizeof (char)); strcpy (string.string, startdir); string.y = 17; string.x = 5; string.w = 70; strcat (string.string, "new.wav"); string.cursorpos = strlen (string.string); string.firstcharonscreen = strlen (string.string) - string.w + 2; if (string.firstcharonscreen < 0) string.firstcharonscreen = 0; ok_button.text = " Start recording "; ok_button.y = 20; ok_button.x = 58; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; mixer_button.text = " Mixer "; mixer_button.y = 20; mixer_button.x = 35; mixer_button.selected = FALSE; clearscreen (RECLP_HEADERTEXT); do { header (RECLP_HEADERTEXT); if (focus == 2) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 3) mixer_button.selected = TRUE; else mixer_button.selected = FALSE; if (focus == 4) ok_button.selected = TRUE; else ok_button.selected = FALSE; dirfilelist.hasfocus = (focus == 0); scrollmenu_display (&dirfilelist); mybox (dirfilelist.y - 1, dirfilelist.x - 1, dirfilelist.h + 2, dirfilelist.w + 2); mvprintw (dirfilelist.y - 1, dirfilelist.x + 1, "Files and directories:"); stringinput_display (&string); mybox (string.y - 1, string.x - 1, 3, string.w + 2); mvprintw (string.y - 1, string.x + 1, "File name:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&mixer_button); mybox (mixer_button.y - 1, mixer_button.x - 1, 3, strlen (mixer_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); helpline (helplines[focus]); if (focus == 1) stringinput_display (&string); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* dirfilelist */ if (scrollmenu_stdkeys (in_ch, &dirfilelist) >= 0) { oldselected = dirfilelist.selected; i = dirfilemenu_process_select (&dirfilelist, helpstring); if (i == 0) /* filename in helpstring */ { strcpy (string.string, helpstring); focus = 1; string.cursorpos = strlen (string.string); string.firstcharonscreen = 0; } else /* dir in helpstring */ { scrollmenu_delete_menu (&dirfilelist); dirfilemenu (helpstring, &dirfilelist); if (dirfilelist.number == 0) { error_window ( "No permission to read this directory."); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = oldselected; } else { strcpy (startdir, helpstring); dirfilelist.firstonscreen = 0; charpointer = strrchr (string.string, '/'); if (charpointer != NULL) strcat (helpstring, charpointer + 1); else strcat (helpstring, string.string); strcpy (string.string, helpstring); } } } else switch (in_ch) { case KEY_LEFT: focus--; break; case KEY_RIGHT: focus++; break; } break; case 1: /* string */ stringinput_stdkeys (in_ch, &string); if (in_ch == KEY_ENTER || in_ch == 13) { strcpy (helpstring, string.string); /* cut away last '/'-s */ while (strlen (helpstring) > 0 && helpstring[strlen (helpstring) - 1] == '/') helpstring[strlen (helpstring) - 1] = '\0'; strcat (helpstring, "/"); if (!stat (helpstring, &filestats) && S_ISDIR (filestats.st_mode)) { strcpy (startdir, helpstring); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.firstonscreen = 0; dirfilelist.selected = dirfilelist.last_of_1st_part + 1; strcpy (string.string, startdir); string.cursorpos = strlen (string.string); focus = 0; } else /* it's a file */ focus = 4; } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 3: /* Mixer */ if (in_ch == KEY_ENTER || in_ch == 13) { system ("xmixer &"); error_window ("Make sure that the appropriate channel is \ selected for recording, and all the others for playback."); } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 4: /* OK */ if (in_ch == KEY_ENTER || in_ch == 13) switch (checkfile (string.string)) { case FILE_EXISTS: if (yesno_window ("The specified file already exists. \ Overwrite it?", " Yes ", " No ", 0)) { strcpy (selectedfile, string.string); returnval = 1; dont_stop = FALSE; } else { string.cursorpos = strlen (string.string); focus = 1; } break; case DIR_EXISTS: error_window ("The specified name is of a directory. A \ file name must be specified."); string.cursorpos = strlen (string.string); focus = 1; break; case DIR_OK_NEW_FILE: strcpy (selectedfile, string.string); returnval = 1; dont_stop = FALSE; break; case DIR_WRONG: error_window ("The directory of the specified file does \ not exist."); string.cursorpos = strlen (string.string); focus = 1; break; default: error_window ("Fell out of switch, reclp_filenm #1"); break; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 4) focus = 0; if (focus < 0) focus = 4; } while (dont_stop); scrollmenu_delete_menu (&dirfilelist); free (string.string); return returnval; } gramofile-1.6/reclp_filenm.h100644 1751 144 667 7070217416 15046 0ustar costarusers/* Record from LP - Get filename - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_RECLP_FILENM_H #define HAVE_RECLP_FILENM_H extern int record_from_lp_get_filename (char *startdir, char *selectedfile); /* Returns 1 if file selected, 0 if canceled */ #endif /* HAVE_RECLP_FILENM_H */ gramofile-1.6/reclp_main.c100644 1751 144 1350 7070217415 14520 0ustar costarusers/* Record from LP * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "reclp_main.h" #include "reclp_filenm.h" #include "errorwindow.h" #include #include #ifndef OLD_CURSES #include #else #include #endif void record_from_lp (char *startdir) { char filename[250]; char shellcmd[500]; if (!record_from_lp_get_filename (startdir, filename)) return; def_prog_mode (); /* save terminal state */ sprintf (shellcmd, "brec_gramo -S -s 44100 -b 16 -t 6000 -w %s", filename); system (shellcmd); reset_prog_mode (); /* reset terminal state */ } gramofile-1.6/reclp_main.h100644 1751 144 613 7070217416 14507 0ustar costarusers/* Record from LP - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_RECLP_MAIN_H #define HAVE_RECLP_MAIN_H #define RECLP_HEADERTEXT "Record audio to a sound file" extern void record_from_lp (char *startdir); #endif /* HAVE_RECLP_MAIN_H */ gramofile-1.6/scrollmenu.c100644 1751 144 5042 7070217415 14574 0ustar costarusers/* Scrolling Menus * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "scrollmenu.h" #include #ifndef OLD_CURSES #include #else #include #endif void scrollmenu_display (scrollmenu_t * menu) { int i, j, x1, y1; if (menu->selected >= menu->number) menu->selected = menu->number - 1; /* May get selected=-1 if number==0... */ if (menu->selected < 0) /* ...so this one must be last! */ menu->selected = 0; if (menu->selected < menu->firstonscreen) menu->firstonscreen = menu->selected; if (menu->selected >= menu->firstonscreen + menu->h) menu->firstonscreen = menu->selected - menu->h + 1; for (i = 0; i < menu->h; i++) { if (menu->firstonscreen + i == menu->selected && menu->hasfocus) attron (A_STANDOUT); if (menu->firstonscreen + i < menu->number) mvaddnstr (menu->y + i, menu->x, menu->items[menu->firstonscreen + i], menu->w); else move (menu->y + i, menu->x); getyx (stdscr, y1, x1); for (j = x1; j < menu->x + menu->w; j++) addch (' '); if (menu->firstonscreen + i == menu->selected && menu->hasfocus) attroff (A_STANDOUT); } move (menu->y + menu->selected - menu->firstonscreen, menu->x); } int scrollmenu_stdkeys (int key, scrollmenu_t * menu) /* Returns >0: item was selected; Returns -1 if nothing serious has happened. */ { int returnval = -1; switch (key) { case 'k': /* Well, `less'-keys here also... */ case 'K': case KEY_UP: (menu->selected)--; break; case 'j': case 'J': case KEY_DOWN: (menu->selected)++; break; case KEY_NPAGE: if (menu->selected < menu->firstonscreen + menu->h - 1) menu->selected = menu->firstonscreen + menu->h - 1; else menu->selected += menu->h - 1; break; case KEY_PPAGE: if (menu->selected > menu->firstonscreen) menu->selected = menu->firstonscreen; else menu->selected -= (menu->h - 1); break; case 13: case KEY_ENTER: returnval = menu->selected; break; } if (menu->selected < 0) menu->selected = 0; if (menu->selected >= menu->number) menu->selected = menu->number - 1; return returnval; } void scrollmenu_delete_menu (scrollmenu_t * menu) { int i; if (menu->items != NULL) { for (i = 0; i < menu->number; i++) free (menu->items[i]); free (menu->items); menu->items = NULL; } } gramofile-1.6/scrollmenu.h100644 1751 144 1535 7070217416 14605 0ustar costarusers/* Scrolling Menus - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SCROLLMENU_H #define HAVE_SCROLLMENU_H typedef struct { char **items; int number; int y; int x; /* (y,x): upper left corner */ int h; int w; /* h=height, w=width */ int selected; int firstonscreen; int last_of_1st_part; /* 'private' for dirfile-menu */ int hasfocus; } scrollmenu_t; extern void scrollmenu_display (scrollmenu_t * menu); extern int scrollmenu_stdkeys (int key, scrollmenu_t * menu); /* Returns >0: item was selected; Returns -1 if nothing serious has happened. */ extern void scrollmenu_delete_menu (scrollmenu_t * menu); #endif /* HAVE_SCROLLMENU_H */ gramofile-1.6/secshms.c100644 1751 144 4067 7070217415 14064 0ustar costarusers/* Seconds to Hour:Minute:Seconds and back * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include #include #include void secs2hms (long seconds, char *outstring) /* 3610 -> 1:00:10 */ { sprintf (outstring, "%ld:%02ld:%02ld", seconds / 3600, (seconds / 60) % 60, seconds % 60); } void fsec2hmsf (double seconds, char *outstring) { double intpart = 0; double floatpart; long i; char helpstring[250]; floatpart = modf (seconds, &intpart); i = intpart; secs2hms (i, outstring); sprintf (helpstring, "%.3f", floatpart); strcat (outstring, helpstring + 1); } int hmsf2fsec (char *instring, double *seconds) /* returns 0: failure, 1: success; instring will be modified. */ { char *charptr; int i = 0; if (!strlen (instring)) return 0; /* empty string */ *seconds = 0; /* seconds */ charptr = strrchr (instring, ':'); if (charptr == NULL) charptr = instring; else { *charptr = '\0'; /* put a '\0' in place of the ':' */ charptr++; } /* charptr is now start of seconds */ if (!sscanf (charptr, "%lf", seconds)) return 0; if (charptr == instring) return 1; /* minutes */ charptr = strrchr (instring, ':'); if (charptr == NULL) charptr = instring; else { *charptr = '\0'; /* put a '\0' in place of the ':' */ charptr++; } /* charptr is now start of minutes */ if (!sscanf (charptr, "%d", &i)) return 0; *seconds += i * 60; if (charptr == instring) return 1; /* hours */ charptr = strrchr (instring, ':'); if (charptr == NULL) charptr = instring; else { *charptr = '\0'; /* put a '\0' in place of the ':' */ charptr++; } /* charptr is now start of hours */ if (!sscanf (charptr, "%d", &i)) return 0; *seconds += i * 3600; if (charptr == instring) return 1; /* nothing before hours: OK */ return 0; /* something before hours. days?? */ } gramofile-1.6/secshms.h100644 1751 144 1076 7070217416 14067 0ustar costarusers/* Seconds to Hour:Minute:Seconds and back - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SECSHMS_H #define HAVE_SECSHMS_H void secs2hms (long seconds, char *outstring); /* 3610 -> 1:00:10 */ void fsec2hmsf (double seconds, char *outstring); /* 10.4 -> 0:00:10.400 */ int hmsf2fsec (char *instring, double *seconds); /* returns 0: failure, 1: success; instring will be modified. */ #endif /* HAVE_SECSHMS_H */ gramofile-1.6/signpr_cmf.c100644 1751 144 44771 7070217415 14574 0ustar costarusers/* Conditional Median Filter * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ /* Remove the `dont' to get b[t].left on left channel and g[t].left on right channel - useful for verifying properties. Note that b[t] is z[t] if RMSlength=RMFlength=1 and b[t] is w[t] if RMFlength=1 (See also Signproc.txt) */ #define dontVIEW_INTERNALS #include "signpr_cmf.h" #include "signpr_general.h" #include "errorwindow.h" #include "stringinput.h" #include "buttons.h" #include "clrscr.h" #include "boxes.h" #include "helpline.h" #include "yesnowindow.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif /* Macros I used first: COND_MEDIAN_MF_POSTLENGTH == parampointer->postlength1 COND_MEDIAN_MF_PRELENGTH == parampointer->prelength1 COND_MEDIAN_RMS_POSTLENGTH == parampointer->postlength2 COND_MEDIAN_RMS_PRELENGTH == parampointer->prelength2 COND_MEDIAN_RMF_POSTLENGTH == parampointer->postlength3 COND_MEDIAN_RMF_PRELENGTH == parampointer->prelength3 COND_MEDIAN_RMF_DECIMATE == parampointer->int1 COND_MEDIAN_THRESHOLD == parampointer->long1 */ void cond_median_param_defaults (parampointer_t parampointer) { /* Best tick-reduction: 21 - 9 - 11 - 5 - 2500 */ parampointer->postlength1 = 10; parampointer->prelength1 = 10; parampointer->postlength2 = 4; parampointer->prelength2 = 4; parampointer->postlength3 = 5; parampointer->prelength3 = 5; parampointer->int1 = 5; /* actually, this should really be 12 */ parampointer->long1 = 2500; /* If you experience badly affected sound, try 15 - 11 - 9 - 4 - 2500 */ } void cond_median_param_screen (parampointer_t parampointer) { stringinput_t medlengthstr, rmslengthstr, rmflengthstr, decimatestr, thresholdstr; button_t ok_button, cancel_button, defaults_button; int dont_stop = TRUE; int focus = 0; int in_ch; int i; long helplong; char *helplines[8] = { " ^: no neat interpolation. v: broad ticks not filtered out. ", " ^: less ticks detected. v: not all of tick interpolated. ", " ^: bad following of dynamics. v: less ticks detected. ", " ^: bad following of dynamics. v: less ticks detected. ", " ^: only strong ticks detected. v: music-ticks also filtered out. ", " Discard changes. ", " Reset default values. ", " Accept changes. "}; medlengthstr.maxlen = 500; medlengthstr.string = (char *) malloc (medlengthstr.maxlen * sizeof (char)); sprintf (medlengthstr.string, "%ld", parampointer->prelength1 + parampointer->postlength1 + 1); medlengthstr.y = 6; medlengthstr.x = 57; medlengthstr.w = 19; medlengthstr.cursorpos = strlen (medlengthstr.string); medlengthstr.firstcharonscreen = 0; rmslengthstr.maxlen = 500; rmslengthstr.string = (char *) malloc (rmslengthstr.maxlen * sizeof (char)); sprintf (rmslengthstr.string, "%ld", parampointer->prelength2 + parampointer->postlength2 + 1); rmslengthstr.y = 8; rmslengthstr.x = 57; rmslengthstr.w = 19; rmslengthstr.cursorpos = strlen (rmslengthstr.string); rmslengthstr.firstcharonscreen = 0; rmflengthstr.maxlen = 500; rmflengthstr.string = (char *) malloc (rmflengthstr.maxlen * sizeof (char)); sprintf (rmflengthstr.string, "%ld", parampointer->prelength3 + parampointer->postlength3 + 1); rmflengthstr.y = 10; rmflengthstr.x = 57; rmflengthstr.w = 19; rmflengthstr.cursorpos = strlen (rmflengthstr.string); rmflengthstr.firstcharonscreen = 0; decimatestr.maxlen = 500; decimatestr.string = (char *) malloc (decimatestr.maxlen * sizeof (char)); sprintf (decimatestr.string, "%d", parampointer->int1); decimatestr.y = 12; decimatestr.x = 57; decimatestr.w = 19; decimatestr.cursorpos = strlen (decimatestr.string); decimatestr.firstcharonscreen = 0; thresholdstr.maxlen = 500; thresholdstr.string = (char *) malloc (thresholdstr.maxlen * sizeof (char)); sprintf (thresholdstr.string, "%ld", parampointer->long1); thresholdstr.y = 14; thresholdstr.x = 57; thresholdstr.w = 19; thresholdstr.cursorpos = strlen (thresholdstr.string); thresholdstr.firstcharonscreen = 0; ok_button.text = " OK "; ok_button.y = 20; ok_button.x = 71; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; defaults_button.text = " Defaults "; defaults_button.y = 20; defaults_button.x = 36; defaults_button.selected = FALSE; clearscreen (SIGNPR_CMF_PARAMSCR_HEADERTEXT); do { header (SIGNPR_CMF_PARAMSCR_HEADERTEXT); if (focus == 5) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 6) defaults_button.selected = TRUE; else defaults_button.selected = FALSE; if (focus == 7) ok_button.selected = TRUE; else ok_button.selected = FALSE; mvprintw (3, 2, "See the Signproc.txt file for the meaning of the parameters."); stringinput_display (&medlengthstr); mvprintw (medlengthstr.y, 2, "Number of samples for median to interpolate ticks:"); stringinput_display (&rmslengthstr); mvprintw (rmslengthstr.y, 2, "Length of the RMS operation (samples):"); stringinput_display (&rmflengthstr); mvprintw (rmflengthstr.y, 2, "Length of the recursive median operation (samples):"); stringinput_display (&decimatestr); mvprintw (decimatestr.y, 2, "Decimation factor for the recursive median:"); stringinput_display (&thresholdstr); mvprintw (thresholdstr.y, 2, "Threshold for tick detection (thousandths):"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&defaults_button); mybox (defaults_button.y - 1, defaults_button.x - 1, 3, strlen (defaults_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); helpline (helplines[focus]); switch (focus) { case 0: stringinput_display (&medlengthstr); break; case 1: stringinput_display (&rmslengthstr); break; case 2: stringinput_display (&rmflengthstr); break; case 3: stringinput_display (&decimatestr); break; case 4: stringinput_display (&thresholdstr); break; default: move (0, 79); } refresh (); in_ch = getch (); switch (focus) { case 0: /* medlengthstr */ stringinput_stdkeys (in_ch, &medlengthstr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (medlengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 1: /* rmslengthstr */ stringinput_stdkeys (in_ch, &rmslengthstr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (rmslengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* rmflengthstr */ stringinput_stdkeys (in_ch, &rmflengthstr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (rmflengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 3: /* decimatestr */ stringinput_stdkeys (in_ch, &decimatestr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (decimatestr.string, "%li", &helplong); if (i < 1 || helplong < 1) error_window ("A whole number, greater than 0, must \ be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 4: /* thresholdstr */ stringinput_stdkeys (in_ch, &thresholdstr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (thresholdstr.string, "%li", &helplong); if (i < 1 || helplong < 1000) error_window ("A whole number, greater than 1000, must \ be specified."); else focus = 7; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 5: /* Cancel */ switch (in_ch) { case KEY_ENTER: case 13: dont_stop = FALSE; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 6: /* Defaults */ switch (in_ch) { case KEY_ENTER: case 13: if (yesno_window ("Restore default parameters?", " Yes ", " No ", 0)) { cond_median_param_defaults (parampointer); dont_stop = FALSE; } break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 7: /* OK */ switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (medlengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as median length."); medlengthstr.cursorpos = strlen (medlengthstr.string); focus = 0; break; } parampointer->prelength1 = (helplong - 1) / 2; parampointer->postlength1 = (helplong - 1) / 2; i = sscanf (rmslengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as RMS length."); rmslengthstr.cursorpos = strlen (rmslengthstr.string); focus = 1; break; } parampointer->prelength2 = (helplong - 1) / 2; parampointer->postlength2 = (helplong - 1) / 2; i = sscanf (rmflengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as length of the recursive median."); rmflengthstr.cursorpos = strlen (rmflengthstr.string); focus = 2; break; } parampointer->prelength3 = (helplong - 1) / 2; parampointer->postlength3 = (helplong - 1) / 2; i = sscanf (decimatestr.string, "%li", &helplong); if (i < 1 || helplong < 1) { error_window ("A whole number, greater than 0, must \ be specified as decimation factor."); decimatestr.cursorpos = strlen (decimatestr.string); focus = 3; break; } parampointer->int1 = helplong; i = sscanf (thresholdstr.string, "%li", &helplong); if (i < 1 || helplong < 1000) { error_window ("A whole number, greater than 1000, must \ be specified as threshold."); thresholdstr.cursorpos = strlen (thresholdstr.string); focus = 4; break; } parampointer->long1 = helplong; dont_stop = FALSE; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 7) focus = 0; if (focus < 0) focus = 7; } while (dont_stop); free (medlengthstr.string); free (rmslengthstr.string); free (rmflengthstr.string); free (decimatestr.string); free (thresholdstr.string); } void init_cond_median_filter (int filterno, parampointer_t parampointer) { long total_post; long total_pre; total_post = parampointer->postlength1; if (parampointer->postlength2 > total_post) total_post = parampointer->postlength2; total_pre = parampointer->prelength1; if (parampointer->prelength2 + parampointer->prelength3 * parampointer->int1 + 1 > total_pre) total_pre = parampointer->prelength2 + parampointer->prelength3 * parampointer->int1 + 1; parampointer->buffer = init_buffer (total_post, total_pre); parampointer->buffer2 = init_buffer (parampointer->postlength2, parampointer->prelength2); parampointer->buffer3 = init_buffer (parampointer->postlength3, parampointer->prelength3 * parampointer->int1); parampointer->filterno = filterno; } void delete_cond_median_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); delete_buffer (¶mpointer->buffer2); delete_buffer (¶mpointer->buffer3); } sample_t cond_median_highpass (long offset, long offset_zero, parampointer_t parampointer) { sample_t sample; longsample_t sum; offset += offset_zero; /* middle for highpass filter in 'big buffer' */ sum.left = 0; sum.right = 0; #define notTEST_DAVE_PLATT #ifndef TEST_DAVE_PLATT sample = get_from_buffer (¶mpointer->buffer, offset - 1); sum.left += sample.left; sum.right += sample.right; sample = get_from_buffer (¶mpointer->buffer, offset); sum.left -= 2 * (long) sample.left; sum.right -= 2 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 1); sum.left += sample.left; sum.right += sample.right; sum.left /= 4; sum.right /= 4; #else /* Testing, suggested by Dave Platt. Invert phase of one channel, then do tick detection using the sum signal. This is because most ticks are out-of-phase signals. I've not really tested this - it might require other settings for thresholds etc. */ sample = get_from_buffer (¶mpointer->buffer, offset - 1); sum.left += sample.left; sum.left -= sample.right; sample = get_from_buffer (¶mpointer->buffer, offset); sum.left -= 2 * (long) sample.left; sum.left += 2 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 1); sum.left += sample.left; sum.left -= sample.right; /* just in case L/R: 32000/-32000 -32000/32000 32000/-32000 : */ sum.left /= 8; sum.right = sum.left; #endif /* TEST_DAVE_PLATT */ sample.left = sum.left; sample.right = sum.right; return sample; } fillfuncpointer_t cond_median_highpass_pointer = cond_median_highpass; sample_t cond_median_rms (long offset, long offset_zero, parampointer_t parampointer) { sample_t sample; doublesample_t doublesample; doublesample_t sum; long i; advance_current_pos_custom (¶mpointer->buffer2, cond_median_highpass_pointer, offset + offset_zero, parampointer); sum.left = 0; sum.right = 0; for (i = -parampointer->postlength2; i <= parampointer->prelength2; i++) { sample = get_from_buffer (¶mpointer->buffer2, i); doublesample.left = sample.left; doublesample.right = sample.right; sum.left += doublesample.left * doublesample.left; sum.right += doublesample.right * doublesample.right; } sum.left /= (parampointer->postlength2 + parampointer->prelength2 + 1); sum.right /= (parampointer->postlength2 + parampointer->prelength2 + 1); sample.left = sqrt (sum.left + 1); sample.right = sqrt (sum.right + 1); return sample; } fillfuncpointer_t cond_median_rms_pointer = cond_median_rms; sample_t cond_median_filter (parampointer_t parampointer) { sample_t sample; sample_t w_t; sample_t b_t; sample_t returnval; signed short list1[parampointer->postlength3 + parampointer->prelength3 * parampointer->int1 + 1]; signed short list2[parampointer->postlength3 + parampointer->prelength3 * parampointer->int1 + 1]; signed short list3[parampointer->postlength1 + parampointer->prelength1 + 1]; long i, j; advance_current_pos (¶mpointer->buffer, parampointer->filterno); advance_current_pos_custom (¶mpointer->buffer3, cond_median_rms_pointer, 0, parampointer); w_t = get_from_buffer (¶mpointer->buffer3, 0); /* The RMF Filter */ for (i = 0; i < parampointer->postlength3; i++) { sample = get_from_buffer (¶mpointer->buffer3, i - parampointer->postlength3); list1[i] = sample.left; list2[i] = sample.right; } j = i; for (; i <= parampointer->postlength3 + parampointer->prelength3 * parampointer->int1; i += parampointer->int1) { sample = get_from_buffer (¶mpointer->buffer3, i - parampointer->postlength3); list1[j] = sample.left; list2[j] = sample.right; j++; } b_t.left = median (list1, j); b_t.right = median (list2, j); put_in_buffer (¶mpointer->buffer3, 0, b_t); #ifdef VIEW_INTERNALS returnval.left = b_t.left * 10; if ( (labs (w_t.left - b_t.left) * 1000) / b_t.left > parampointer->long1) returnval.right = 2000; else returnval.right = 0; #else /* not VIEW_INTERNALS */ returnval = get_from_buffer (¶mpointer->buffer, 0); /* Median Filters - if necessary */ if ( (labs (w_t.left - b_t.left) * 1000) / b_t.left > parampointer->long1) { for (i = 0; i <= parampointer->postlength1 + parampointer->prelength1; i++) list3[i] = get_from_buffer (¶mpointer->buffer, i - parampointer->postlength1).left; returnval.left = median (list3, parampointer->postlength1 + parampointer->prelength1 + 1); } if ( (labs (w_t.right - b_t.right) * 1000) / b_t.right > parampointer->long1) { for (i = 0; i <= parampointer->postlength1 + parampointer->prelength1; i++) list3[i] = get_from_buffer (¶mpointer->buffer, i - parampointer->postlength1).right; returnval.right = median (list3, parampointer->postlength1 + parampointer->prelength1 + 1); } #endif /* VIEW_INTERNALS */ return returnval; } gramofile-1.6/signpr_cmf.h100644 1751 144 1334 7070217416 14546 0ustar costarusers/* Simple Median Filter - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_CMF_H #define HAVE_SIGNPR_CMF_H #include "signpr_general.h" #define SIGNPR_CMF_PARAMSCR_HEADERTEXT "Conditional Median Filter - Parameters" void cond_median_param_defaults (parampointer_t parampointer); void cond_median_param_screen (parampointer_t parampointer); void init_cond_median_filter (int filterno, parampointer_t parampointer); void delete_cond_median_filter (parampointer_t parampointer); sample_t cond_median_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_CMF_H */ gramofile-1.6/signpr_filtmenu.c100644 1751 144 34555 7070217415 15651 0ustar costarusers/* Signal Processing - Filter Menu * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_filtmenu.h" #include "signpr_general.h" #include "buttons.h" #include "boxes.h" #include "helpline.h" #include "clrscr.h" #include "errorwindow.h" #include "stringinput.h" #include "secshms.h" #include #include #ifndef OLD_CURSES #include #else #include #endif int signproc_select_filters (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts, scrollmenu_t * selectedfilts, int *usetracktimes, int *usebeginendtime, double *begintime, double *endtime) /* Returns 0: canceled, 1: PrevScreen, 2: NextScreen */ { button_t next_button, cancel_button, prev_button; button_t beginend_check, tracktimes_check; stringinput_t begintimestring, endtimestring; int dont_stop = TRUE; int returnval = 0; int focus = 0; int in_ch; int i; double tempdouble = 0; char tempstring[250]; char *emptyhelpline = " TAB: Next field"; int maxhelplength = 62; char helphelpline[100]; char *helplines[9] = { " Enter: Add selected filter. TAB: Next field", " Enter: Parameters U/D: Move R/Del: Delete TAB: Next field", " Use the .tracks file to split tracks. TAB: Next field", " Process only the specified part of the sound. TAB: Next field", " Enter begin time of part to be processed. TAB: Next field", " Enter end time of part to be processed. TAB: Next field", " To Signal Processing - Source File. TAB: Next field", " Back to main menu. TAB: Next field", " To Signal Processing - Destination File. TAB: Next field"}; char *helpcharptr; int helpint; parampointer_t helpparampointer; filtlist->y = 3; filtlist->x = 5; filtlist->h = 12; filtlist->w = 32; filtlist->firstonscreen = 0; filtlist->selected = 0; selectedfilts->y = 3; selectedfilts->x = 43; selectedfilts->h = 12; selectedfilts->w = 32; selectedfilts->firstonscreen = 0; selectedfilts->selected = 0; begintimestring.maxlen = 500; begintimestring.string = (char *) malloc ( begintimestring.maxlen * sizeof (char)); fsec2hmsf (*begintime, begintimestring.string); begintimestring.y = 17; begintimestring.x = 59; begintimestring.w = 18; begintimestring.cursorpos = strlen (begintimestring.string); begintimestring.firstcharonscreen = 0; endtimestring.maxlen = 500; endtimestring.string = (char *) malloc ( endtimestring.maxlen * sizeof (char)); fsec2hmsf (*endtime, endtimestring.string); endtimestring.y = 18; endtimestring.x = 59; endtimestring.w = 18; endtimestring.cursorpos = strlen (endtimestring.string); endtimestring.firstcharonscreen = 0; prev_button.text = " < Previous screen "; prev_button.y = 20; prev_button.x = 5; prev_button.selected = FALSE; next_button.text = " Next screen > "; next_button.y = 20; next_button.x = 60; next_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 36; cancel_button.selected = FALSE; tracktimes_check.text = ""; /* see below */ tracktimes_check.y = 16; tracktimes_check.x = 4; tracktimes_check.selected = FALSE; beginend_check.text = ""; /* see below */ beginend_check.y = 16; beginend_check.x = 42; beginend_check.selected = FALSE; clearscreen (SIGNPR_FILTMENU_HEADERTEXT); do { header (SIGNPR_FILTMENU_HEADERTEXT); if (*usetracktimes) tracktimes_check.text = "[X] Split tracks"; else tracktimes_check.text = "[ ] Split tracks"; if (*usebeginendtime) beginend_check.text = "[X] Use begin and end times"; else beginend_check.text = "[ ] Use begin and end times"; if (focus == 2) tracktimes_check.selected = TRUE; else tracktimes_check.selected = FALSE; if (focus == 3) beginend_check.selected = TRUE; else beginend_check.selected = FALSE; if (focus == 6) prev_button.selected = TRUE; else prev_button.selected = FALSE; if (focus == 7) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 8) next_button.selected = TRUE; else next_button.selected = FALSE; filtlist->hasfocus = (focus == 0); selectedfilts->hasfocus = (focus == 1); scrollmenu_display (filtlist); mybox (filtlist->y - 1, filtlist->x - 1, filtlist->h + 2, filtlist->w + 2); mvprintw (filtlist->y - 1, filtlist->x + 1, "Available filters:"); scrollmenu_display (selectedfilts); mybox (selectedfilts->y - 1, selectedfilts->x - 1, selectedfilts->h + 2, selectedfilts->w + 2); mvprintw (selectedfilts->y - 1, selectedfilts->x + 1, "Selected filters:"); stringinput_display (&begintimestring); mvprintw (begintimestring.y, begintimestring.x - 12, "Begin time:"); stringinput_display (&endtimestring); mvprintw (endtimestring.y, endtimestring.x - 12, "End time :"); button_display (&prev_button); mybox (prev_button.y - 1, prev_button.x - 1, 3, strlen (prev_button.text) + 2); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&next_button); mybox (next_button.y - 1, next_button.x - 1, 3, strlen (next_button.text) + 2); button_display (&tracktimes_check); button_display (&beginend_check); if (focus == 0) { strcpy (helphelpline, emptyhelpline); i = 0; while (i < strlen (helptexts[filtlist->selected]) && i < maxhelplength) { helphelpline[i + 1] = helptexts[filtlist->selected][i]; i++; } helpline (helphelpline); } else helpline (helplines[focus]); if (focus == 4) stringinput_display (&begintimestring); else if (focus == 5) stringinput_display (&endtimestring); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* filtlist */ if (scrollmenu_stdkeys (in_ch, filtlist) >= 0) { if (selectedfilts->number < MAX_FILTERS) { selectedfilts->items[number_of_filters] = filtlist->items[filtlist->selected]; filter_type[number_of_filters] = filtnumbers[filtlist->selected]; parampointerarray[number_of_filters] = (parampointer_t) malloc ( sizeof (param_t)); param_defaults ( parampointerarray[number_of_filters], filtnumbers[filtlist->selected]); number_of_filters++; selectedfilts->number = number_of_filters; selectedfilts->selected = number_of_filters - 1; } else error_window ("The maximum number of filters has been \ reached. No more filters can be added."); } else switch (in_ch) { case KEY_LEFT: focus--; break; case KEY_RIGHT: focus++; break; } break; case 1: /* selectedfilts */ if (scrollmenu_stdkeys (in_ch, selectedfilts) >= 0) { if (number_of_filters > 0) ; param_screen ( parampointerarray[selectedfilts->selected], filter_type[selectedfilts->selected]); } else switch (in_ch) { case 'u': case 'U': if (selectedfilts->selected > 0) { helpcharptr = selectedfilts->items[ selectedfilts->selected - 1]; selectedfilts->items[selectedfilts->selected - 1] = selectedfilts->items[selectedfilts->selected]; selectedfilts->items[selectedfilts->selected] = helpcharptr; helpint = filter_type[selectedfilts->selected - 1]; filter_type[selectedfilts->selected - 1] = filter_type[selectedfilts->selected]; filter_type[selectedfilts->selected] = helpint; helpparampointer = parampointerarray[ selectedfilts->selected - 1]; parampointerarray[selectedfilts->selected - 1] = parampointerarray[selectedfilts->selected]; parampointerarray[selectedfilts->selected] = helpparampointer; selectedfilts->selected--; } break; case 'd': case 'D': if (selectedfilts->selected < number_of_filters - 1) { helpcharptr = selectedfilts->items[ selectedfilts->selected + 1]; selectedfilts->items[selectedfilts->selected + 1] = selectedfilts->items[selectedfilts->selected]; selectedfilts->items[selectedfilts->selected] = helpcharptr; helpint = filter_type[selectedfilts->selected + 1]; filter_type[selectedfilts->selected + 1] = filter_type[selectedfilts->selected]; filter_type[selectedfilts->selected] = helpint; helpparampointer = parampointerarray[ selectedfilts->selected + 1]; parampointerarray[selectedfilts->selected + 1] = parampointerarray[selectedfilts->selected]; parampointerarray[selectedfilts->selected] = helpparampointer; selectedfilts->selected++; } break; case KEY_BACKSPACE: case 127: case 'r': case 'R': if (number_of_filters > 0) { free (parampointerarray[selectedfilts->selected]); for (i = selectedfilts->selected; i < number_of_filters - 1; i++) { selectedfilts->items[i] = selectedfilts->items[i + 1]; filter_type[i] = filter_type[i + 1]; parampointerarray[i] = parampointerarray[i + 1]; } number_of_filters--; selectedfilts->number = number_of_filters; } break; case KEY_LEFT: focus--; break; case KEY_RIGHT: focus++; break; } break; case 2: /* Use tracksplitting data */ switch (in_ch) { case KEY_ENTER: case 13: case ' ': case 'x': case 'X': *usetracktimes = 1 - *usetracktimes; if (*usetracktimes) *usebeginendtime = 0; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 3: /* Use begin/end times */ switch (in_ch) { case KEY_ENTER: case 13: case ' ': case 'x': case 'X': *usebeginendtime = 1 - *usebeginendtime; if (*usebeginendtime) *usetracktimes = 0; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 4: /* begin time */ stringinput_stdkeys (in_ch, &begintimestring); switch (in_ch) { case KEY_ENTER: case 13: strcpy (tempstring, begintimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); begintimestring.cursorpos = strlen (begintimestring.string); } else { *begintime = tempdouble; fsec2hmsf (tempdouble, begintimestring.string); begintimestring.cursorpos = strlen (begintimestring.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 5: /* end time */ stringinput_stdkeys (in_ch, &endtimestring); switch (in_ch) { case KEY_ENTER: case 13: strcpy (tempstring, endtimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); endtimestring.cursorpos = strlen (endtimestring.string); } else { *endtime = tempdouble; fsec2hmsf (tempdouble, endtimestring.string); endtimestring.cursorpos = strlen (endtimestring.string); focus = 8; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 6: /* < Previous */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 1; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 7: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 8: /* Next > */ if (in_ch == KEY_ENTER || in_ch == 13) { if (number_of_filters < 1) { error_window ("No filters have been selected."); focus = 0; break; } strcpy (tempstring, begintimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the begin time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); begintimestring.cursorpos = strlen (begintimestring.string); focus = 4; break; } *begintime = tempdouble; strcpy (tempstring, endtimestring.string); if (!hmsf2fsec (tempstring, &tempdouble)) { error_window ("Enter the end time in the format hours:\ minutes:seconds.fractions, for example 0:04:26.740"); endtimestring.cursorpos = strlen (endtimestring.string); focus = 5; break; } *endtime = tempdouble; if (*begintime > *endtime) { error_window ("The begin time is larger than the end time."); fsec2hmsf (*begintime, begintimestring.string); begintimestring.cursorpos = strlen (begintimestring.string); fsec2hmsf (*endtime, endtimestring.string); endtimestring.cursorpos = strlen (endtimestring.string); focus = 4; break; } returnval = 2; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } /* switch(focus) */ if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 8) focus -= 9; if (focus < 0) focus += 9; } while (dont_stop); return returnval; } gramofile-1.6/signpr_filtmenu.h100644 1751 144 1607 7070217416 15627 0ustar costarusers/* Signal Processing - Filter Menu - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_FILTMENU_H #define HAVE_SIGNPR_FILTMENU_H #include "scrollmenu.h" #define SIGNPR_FILTMENU_HEADERTEXT "Signal Processing - Filter Selection" void add_to_filterlist (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts, int filternumber, char *filtername, char *helptext); void make_filterlist (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts); int signproc_select_filters (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts, scrollmenu_t * selectedfilts, int *usetracktimes, int *usebeginendtime, double *begintime, double *endtime); #endif /* HAVE_SIGNPR_FILTMENU_H */ gramofile-1.6/signpr_general.c100644 1751 144 36307 7070217416 15441 0ustar costarusers/* General functions for signal processing * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_general.h" #include "scrollmenu.h" #include "signpr_median.h" #include "signpr_wav.h" #include "signpr_cmf.h" #include "signpr_cmf2.h" #include "signpr_mean.h" #include "signpr_doubmed.h" #include "signpr_rms.h" #include "signpr_copy.h" #include "signpr_exper.h" #include "signpr_mono.h" #include #include #ifndef OLD_CURSES #include #else #include #endif void write_sample_to_screen (sample_t data) { printf ("Output, left & right: %d %d\n", data.left, data.right); } sample_t read_from_keyboard () { sample_t inputsample; signed int left; /* 'standard int' for %d */ signed int right; printf ("Please enter left & right sample: "); scanf ("%d %d", &(left), &(right)); inputsample.left = left; inputsample.right = right; return inputsample; } parampointer_t parampointerarray[MAX_FILTERS]; int filter_type[MAX_FILTERS]; int number_of_filters; /* ----- BUFFER ------------------------------------------------------------ */ buffer_t init_buffer (long post_length, long pre_length) { buffer_t newbuffer; long bufferlength; bufferlength = pre_length + post_length + 1; newbuffer.array = (sample_t *) malloc (bufferlength * sizeof (sample_t)); newbuffer.currpos = -1; newbuffer.arraylength = bufferlength; newbuffer.pre_length = pre_length; newbuffer.post_length = post_length; #ifdef TURBO_BUFFER { int i; int tablesize = bufferlength * 3; newbuffer.indextable = (int *) malloc (tablesize * sizeof (int)); newbuffer.indextable += bufferlength; for (i = -bufferlength; i < 2 * bufferlength; i++) { newbuffer.indextable[i] = (i + bufferlength) % bufferlength; } } #endif return newbuffer; } void delete_buffer (buffer_t * buffer) { #ifdef TURBO_BUFFER free (buffer->indextable - buffer->arraylength); #endif buffer->arraylength = 0; buffer->pre_length = 0; buffer->post_length = 0; free (buffer->array); } #ifndef TURBO_BUFFER sample_t get_from_buffer (buffer_t * buffer, long offset) { long realpos; if ((offset > buffer->pre_length) || (-offset > buffer->post_length)) { fprintf (stderr, "ERROR: get_from_buffer #1 - offset=%ld\n", offset); } realpos = buffer->currpos + offset; if (realpos < 0) realpos += buffer->arraylength; if (realpos >= buffer->arraylength) realpos -= buffer->arraylength; return (buffer->array)[realpos]; } void put_in_buffer (buffer_t * buffer, long offset, sample_t sample) { long realpos; if ((offset > buffer->pre_length) || (-offset > buffer->post_length)) { fprintf (stderr, "ERROR (put_in_buffer #1)\n"); } realpos = buffer->currpos + offset; if (realpos < 0) realpos += buffer->arraylength; if (realpos >= buffer->arraylength) realpos -= buffer->arraylength; (buffer->array)[realpos] = sample; } #endif /* ndef TURBO_BUFFER */ void advance_current_pos (buffer_t * buffer, int filterno) { long i; if (buffer->currpos < 0) /* just newly created */ { for (i = 0; i <= buffer->post_length; i++) { /* fill first part with zeroes */ (buffer->array)[i].left = 0; (buffer->array)[i].right = 0; } buffer->currpos = buffer->post_length; for (i = 0; i <= buffer->pre_length; i++) { (buffer->array)[buffer->currpos + i] = get_sample_from_filter (filterno - 1); } } else { buffer->currpos++; if (buffer->currpos >= buffer->arraylength) buffer->currpos -= buffer->arraylength; i = buffer->currpos + buffer->pre_length; if (i >= buffer->arraylength) i -= buffer->arraylength; (buffer->array)[i] = get_sample_from_filter (filterno - 1); } } void advance_current_pos_custom (buffer_t * buffer, fillfuncpointer_t fillfunc, long offset_zero, parampointer_t parampointer) { long i; if (buffer->currpos < 0) /* just newly created */ { for (i = 0; i <= buffer->post_length; i++) { /* fill first part with zeroes */ (buffer->array)[i].left = 0; (buffer->array)[i].right = 0; } buffer->currpos = buffer->post_length; for (i = 0; i <= buffer->pre_length; i++) { (buffer->array)[buffer->currpos + i] = fillfunc (i, offset_zero, parampointer); } } else { buffer->currpos++; if (buffer->currpos >= buffer->arraylength) buffer->currpos -= buffer->arraylength; i = buffer->currpos + buffer->pre_length; if (i >= buffer->arraylength) i -= buffer->arraylength; (buffer->array)[i] = fillfunc (buffer->pre_length, offset_zero, parampointer); } } /* ----- QUICK SORT -------------------------------------------------------- */ /* For arrays of `signed short' with max. length of 32767. */ /* Adapted from Ammeraal, L., `Ansi C', 2nd ed., Schoonhoven (The Netherlands): Academic Service, 1993. */ void qsort2 (signed short *a, int n) /* a: pointer to start of array */ { /* n: # elements in array */ int i, j; signed short x, w; do { i = 0; j = n - 1; x = a[j / 2]; do { while (a[i] < x) i++; while (a[j] > x) j--; if (i > j) break; w = a[i]; a[i] = a[j]; a[j] = w; } while (++i <= --j); if (j + 1 < n - i) { if (j > 0) qsort2 (a, j + 1); a += i; n -= i; } else { if (i < n - 1) qsort2 (a + i, n - i); n = j + 1; } } while (n > 1); } /* And one for doubles, max size 2G */ void qsort2double (double *a, long n) /* a: pointer to start of array */ { /* n: # elements in array */ long i, j; double x, w; do { i = 0; j = n - 1; x = a[j / 2]; do { while (a[i] < x) i++; while (a[j] > x) j--; if (i > j) break; w = a[i]; a[i] = a[j]; a[j] = w; } while (++i <= --j); if (j + 1 < n - i) { if (j > 0) qsort2double (a, j + 1); a += i; n -= i; } else { if (i < n - 1) qsort2double (a + i, n - i); n = j + 1; } } while (n > 1); } /* ----- MEDIAN FUNCTION --------------------------------------------------- */ /* For arrays of 'signed short' with max. length of 32767. NOTE: The array will be affected (most likely: sorted). Do not use the array after calling median() ! Examples: {2,5,4,1,3} ==sort==> {1,2,3,4,5} ==returns==> 3 {2,5,4,1,6,3} ==sort==> {1,2,3,4,5,6} ==returns==> 3 */ #ifdef TURBO_MEDIAN #ifndef MEDIAN_THRESHOLD #define MEDIAN_THRESHOLD 5 #endif signed short median (signed short *a, int n) { int i, j, k; signed short x, w, t1, t2; int low, mid, high; low = 0; mid = n / 2; high = n - 1; while (high - low > MEDIAN_THRESHOLD) { i = low; j = high; t1 = a[i]; t2 = a[j]; if (t1 > t2) { x = t1; t1 = t2; t2 = x; } x = a[(low + high) / 2]; if (x < t1) { x = t1; } else if (x > t2) { x = t2; } do { while (a[i] < x) i++; while (a[j] > x) j--; if (i > j) break; w = a[i]; a[i] = a[j]; a[j] = w; } while (++i <= --j); if (i <= mid) { low = i; } else { high = j; } } for (i = low; i <= mid; i++) { k = i; w = a[i]; for (j = i + 1; j <= high; j++) { if (a[j] < w) { k = j; w = a[j]; } } if (k != i) { a[k] = a[i]; a[i] = w; } } return a[mid]; } #else /* if not TURBO_MEDIAN */ signed short median (signed short *a, int n) { /* a: pointer to start of array */ qsort2 (a, n); /* n: # elements in array */ return a[((n + 1) / 2) - 1]; /* (10+1)/2 = 5 (11+1)/2 = 6 */ } #endif /* (not) TURBO_MEDIAN */ /* ----- BUILDING THE LIST OF FILTERS -------------------------------------- */ void add_to_filterlist (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts, int filternumber, char *filtername, char *helptext) { filtlist->items[filtlist->number] = filtername; filtnumbers[filtlist->number] = filternumber; helptexts[filtlist->number] = helptext; filtlist->number++; } void make_filterlist (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts) { filtlist->number = 0; add_to_filterlist (filtlist, filtnumbers, helptexts, COPYONLY_FILTER, COPYONLY_NAME, COPYONLY_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, MONOIZE_FILTER, MONOIZE_NAME, MONOIZE_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, SIMPLE_MEDIAN_FILTER, SIMPLE_MEDIAN_NAME, SIMPLE_MEDIAN_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, DOUBLE_MEDIAN_FILTER, DOUBLE_MEDIAN_NAME, DOUBLE_MEDIAN_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, SIMPLE_MEAN_FILTER, SIMPLE_MEAN_NAME, SIMPLE_MEAN_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, RMS_FILTER, RMS_NAME, RMS_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, COND_MEDIAN_FILTER, COND_MEDIAN_NAME, COND_MEDIAN_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, COND_MEDIAN2_FILTER, COND_MEDIAN2_NAME, COND_MEDIAN2_HELPTEXT); add_to_filterlist (filtlist, filtnumbers, helptexts, EXPERIMENT_FILTER, EXPERIMENT_NAME, EXPERIMENT_HELPTEXT); } /* ----- GET SAMPLE FROM FILTER -------------------------------------------- */ sample_t get_sample_from_filter (int filterno) { if (filterno == -1) return readsamplesource (); else switch (filter_type[filterno]) { case SIMPLE_MEDIAN_FILTER: return simple_median_filter (parampointerarray[filterno]); break; case SIMPLE_MEAN_FILTER: return simple_mean_filter (parampointerarray[filterno]); break; case COND_MEDIAN_FILTER: return cond_median_filter (parampointerarray[filterno]); break; case DOUBLE_MEDIAN_FILTER: return double_median_filter (parampointerarray[filterno]); break; case COND_MEDIAN2_FILTER: return cond_median2_filter (parampointerarray[filterno]); break; case RMS_FILTER: return rms_filter (parampointerarray[filterno]); break; case COPYONLY_FILTER: return copyonly_filter (parampointerarray[filterno]); break; case MONOIZE_FILTER: return monoize_filter (parampointerarray[filterno]); break; case EXPERIMENT_FILTER: return experiment_filter (parampointerarray[filterno]); break; default: printf ("Error (get_sample_from_filter): wrong filter"); exit (2); break; } } /* ----- INIT FILTERS ------------------------------------------------------ */ void init_filters () { int i; for (i = 0; i < number_of_filters; i++) switch (filter_type[i]) { case 0: break; case SIMPLE_MEDIAN_FILTER: init_simple_median_filter (i, parampointerarray[i]); break; case SIMPLE_MEAN_FILTER: init_simple_mean_filter (i, parampointerarray[i]); break; case COND_MEDIAN_FILTER: init_cond_median_filter (i, parampointerarray[i]); break; case DOUBLE_MEDIAN_FILTER: init_double_median_filter (i, parampointerarray[i]); break; case COND_MEDIAN2_FILTER: init_cond_median2_filter (i, parampointerarray[i]); break; case RMS_FILTER: init_rms_filter (i, parampointerarray[i]); break; case COPYONLY_FILTER: init_copyonly_filter (i, parampointerarray[i]); break; case MONOIZE_FILTER: init_monoize_filter (i, parampointerarray[i]); break; case EXPERIMENT_FILTER: init_experiment_filter (i, parampointerarray[i]); break; default: printf ("Error (init_filters): wrong filter"); exit (2); break; } } /* ----- DELETE FILTERS ------------------------------------------------------ */ void delete_filters () { int i; for (i = 0; i < number_of_filters; i++) switch (filter_type[i]) { case 0: break; case SIMPLE_MEDIAN_FILTER: delete_simple_median_filter (parampointerarray[i]); break; case SIMPLE_MEAN_FILTER: delete_simple_mean_filter (parampointerarray[i]); break; case COND_MEDIAN_FILTER: delete_cond_median_filter (parampointerarray[i]); break; case DOUBLE_MEDIAN_FILTER: delete_double_median_filter (parampointerarray[i]); break; case COND_MEDIAN2_FILTER: delete_cond_median2_filter (parampointerarray[i]); break; case RMS_FILTER: delete_rms_filter (parampointerarray[i]); break; case COPYONLY_FILTER: delete_copyonly_filter (parampointerarray[i]); break; case MONOIZE_FILTER: delete_monoize_filter (parampointerarray[i]); break; case EXPERIMENT_FILTER: delete_experiment_filter (parampointerarray[i]); break; default: printf ("Error (delete_filters): wrong filter"); exit (2); break; } } /* ----- PARAM DEFAULTS ---------------------------------------------------- */ void param_defaults (parampointer_t parampointer, int filtertype) { switch (filtertype) { case 0: /* nothing needed */ break; case SIMPLE_MEDIAN_FILTER: simple_median_param_defaults (parampointer); break; case SIMPLE_MEAN_FILTER: simple_mean_param_defaults (parampointer); break; case COND_MEDIAN_FILTER: cond_median_param_defaults (parampointer); break; case DOUBLE_MEDIAN_FILTER: double_median_param_defaults (parampointer); break; case COND_MEDIAN2_FILTER: cond_median2_param_defaults (parampointer); break; case RMS_FILTER: rms_param_defaults (parampointer); break; case COPYONLY_FILTER: copyonly_param_defaults (parampointer); break; case MONOIZE_FILTER: monoize_param_defaults (parampointer); break; case EXPERIMENT_FILTER: experiment_param_defaults (parampointer); break; default: printf ("Error (praram_defaults): wrong filter"); exit (2); break; } } /* ----- PARAM SCREEN ------------------------------------------------------ */ void param_screen (parampointer_t parampointer, int filtertype) { switch (filtertype) { case 0: /* nothing needed */ break; case SIMPLE_MEDIAN_FILTER: simple_median_param_screen (parampointer); break; case SIMPLE_MEAN_FILTER: simple_mean_param_screen (parampointer); break; case COND_MEDIAN_FILTER: cond_median_param_screen (parampointer); break; case DOUBLE_MEDIAN_FILTER: double_median_param_screen (parampointer); break; case COND_MEDIAN2_FILTER: cond_median2_param_screen (parampointer); break; case RMS_FILTER: rms_param_screen (parampointer); break; case COPYONLY_FILTER: copyonly_param_screen (parampointer); break; case MONOIZE_FILTER: monoize_param_screen (parampointer); break; case EXPERIMENT_FILTER: experiment_param_screen (parampointer); break; default: printf ("Error (praram_screen): wrong filter"); exit (2); break; } clear (); refresh (); } gramofile-1.6/signpr_general.h100644 1751 144 11521 7070217416 15435 0ustar costarusers/* General functions for signal processing - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_GENERAL_H #define HAVE_SIGNPR_GENERAL_H #include "scrollmenu.h" /* SAMPLES */ typedef struct { signed short left; signed short right; } sample_t; typedef struct { signed long left; signed long right; } longsample_t; typedef struct { double left; double right; } doublesample_t; /* BUFFER */ typedef struct { sample_t *array; long currpos; long arraylength; long pre_length; /* "read ahead" */ long post_length; /* "remember" */ #ifdef TURBO_BUFFER int *indextable; #endif } buffer_t; /* PARAM */ typedef struct { buffer_t buffer; buffer_t buffer2; buffer_t buffer3; buffer_t buffer4; int filterno; /* 'serial number' of filter, 0=read_from_disk */ long postlength1, prelength1, postlength2, prelength2, postlength3, prelength3, postlength4, prelength4; signed short *sslist1; signed short *sslist2; int int1; long long1; long long2; } param_t; typedef param_t *parampointer_t; /* FILTER DATA */ #define MAX_FILTERS 50 extern parampointer_t parampointerarray[MAX_FILTERS]; extern int filter_type[MAX_FILTERS]; extern int number_of_filters; /* BASIC SCREEN I/O */ void write_sample_to_screen (sample_t data); sample_t read_from_keyboard (); /* BUFFER FILL FUNCTION */ typedef sample_t (*fillfuncpointer_t) (long offset, long offset_zero, parampointer_t parampointer); /* BUFFER FUNCTIONS */ buffer_t init_buffer (long post_length, long pre_length); void delete_buffer (buffer_t * buffer); #ifdef TURBO_BUFFER #define get_from_buffer(buffer,offset) (buffer)->array[(buffer)->indextable[(buffer)->currpos+offset]] #define put_in_buffer(buffer,offset,sample) (buffer)->array[(buffer)->indextable[(buffer)->currpos+offset]]=sample #else /* if not TURBO_BUFFER */ sample_t get_from_buffer (buffer_t * buffer, long offset); void put_in_buffer (buffer_t * buffer, long offset, sample_t sample); #endif /* (not) TURBO_BUFFER */ void advance_current_pos (buffer_t * buffer, int filterno); void advance_current_pos_custom (buffer_t * buffer, fillfuncpointer_t fillfunc, long offset_zero, parampointer_t parampointer); /* QUICK SORT */ /* One for signed shorts, max size 32676 */ void qsort2 (signed short *a, int n); /* a: pointer to start of array */ /* n: # elements in array */ /* And one for doubles, max size 2G */ void qsort2double (double *a, long n); /* a: pointer to start of array */ /* n: # elements in array */ /* MEDIAN */ signed short median (signed short *a, int n); /* BUILDING THE LIST OF FILTERS */ void add_to_filterlist (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts, int filternumber, char *filtername, char *helptext); void make_filterlist (scrollmenu_t * filtlist, int *filtnumbers, char **helptexts); /* GET SAMPLE FROM FILTER */ sample_t get_sample_from_filter (int filterno); /* INIT & DELETE FILTERS */ void init_filters (); void delete_filters (); /* PARAM DEFAULTS */ void param_defaults (parampointer_t parampointer, int filtertype); /* PARAM SCREENS */ void param_screen (parampointer_t parampointer, int filtertype); /* FILTER NUMBERS ETC. */ #define SIMPLE_MEDIAN_FILTER 1 #define SIMPLE_MEDIAN_NAME "Simple Median Filter" #define SIMPLE_MEDIAN_HELPTEXT \ "Interpolate short ticks." #define SIMPLE_MEAN_FILTER 2 #define SIMPLE_MEAN_NAME "Simple Mean Filter" #define SIMPLE_MEAN_HELPTEXT \ "'Smooth' the signal by taking the mean of samples." #define COND_MEDIAN_FILTER 3 #define COND_MEDIAN_NAME "Conditional Median Filter" #define COND_MEDIAN_HELPTEXT \ "Remove ticks while not changing rest of signal." #define DOUBLE_MEDIAN_FILTER 4 #define DOUBLE_MEDIAN_NAME "Double Median Filter" #define DOUBLE_MEDIAN_HELPTEXT \ "Interpolate short ticks and correct interpolations." #define COND_MEDIAN2_FILTER 5 #define COND_MEDIAN2_NAME "Conditional Median Filter II" #define COND_MEDIAN2_HELPTEXT \ "Remove ticks while not changing rest of signal - Better." #define RMS_FILTER 6 #define RMS_NAME "RMS Filter" #define RMS_HELPTEXT \ "Compute the `running' Root-Mean-Square of the signal." #define COPYONLY_FILTER 7 #define COPYONLY_NAME "Copy Only" #define COPYONLY_HELPTEXT \ "Do nothing - just copy the signal unchanged." #define MONOIZE_FILTER 8 #define MONOIZE_NAME "Convert to mono" #define MONOIZE_HELPTEXT \ "Average left & right signals." #define EXPERIMENT_FILTER 9 #define EXPERIMENT_NAME "Experimenting Filter" #define EXPERIMENT_HELPTEXT \ "The filter YOU are experimenting with (in signpr_exper.c)" #endif /* HAVE_SIGNPR_GENERAL_H */ gramofile-1.6/signpr_infilenm.c100644 1751 144 16107 7070217416 15621 0ustar costarusers/* Signal Processing - Get infile-name * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_infilenm.h" #include "scrollmenu.h" #include "stringinput.h" #include "buttons.h" #include "boxes.h" #include "dirfilemenu.h" #include "errorwindow.h" #include "textwindow.h" #include "checkfile.h" #include "yesnowindow.h" #include "helpline.h" #include "clrscr.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif int signproc_select_infile (char *startdir, char *selectedfile) /* Returns 0: canceled, 2: NextScreen */ { scrollmenu_t dirfilelist; stringinput_t string; button_t next_button, cancel_button; int dont_stop = TRUE; int returnval = 0; int focus = 1; int in_ch; int i; char helpstring[500]; char *charpointer; struct stat filestats; int oldselected; char *helplines[4] = { " Select name of sound file to be processed. TAB: Next field", " Enter name of sound file to be processed. TAB: Next field", " Back to main menu. TAB: Next field", " To Signal Processing - Filter Selection. TAB: Next field"}; dirfilelist.y = 3; dirfilelist.x = 5; dirfilelist.h = 12; dirfilelist.w = 32; dirfilelist.firstonscreen = 0; dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = dirfilelist.last_of_1st_part + 1; string.maxlen = 500; string.string = (char *) malloc (string.maxlen * sizeof (char)); if (selectedfile[0] == '\0') strcpy (string.string, startdir); else strcpy (string.string, selectedfile); string.y = 17; string.x = 5; string.w = 70; string.cursorpos = strlen (string.string); string.firstcharonscreen = strlen (string.string) - string.w + 2; if (string.firstcharonscreen < 0) string.firstcharonscreen = 0; next_button.text = " Next screen > "; next_button.y = 20; next_button.x = 60; next_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 36; cancel_button.selected = FALSE; clearscreen (SIGNPR_INFILE_HEADERTEXT); do { if (focus == 2) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 3) next_button.selected = TRUE; else next_button.selected = FALSE; dirfilelist.hasfocus = (focus == 0); scrollmenu_display (&dirfilelist); mybox (dirfilelist.y - 1, dirfilelist.x - 1, dirfilelist.h + 2, dirfilelist.w + 2); mvprintw (dirfilelist.y - 1, dirfilelist.x + 1, "Files and directories:"); stringinput_display (&string); mybox (string.y - 1, string.x - 1, 3, string.w + 2); mvprintw (string.y - 1, string.x + 1, "Source file name:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&next_button); mybox (next_button.y - 1, next_button.x - 1, 3, strlen (next_button.text) + 2); helpline (helplines[focus]); if (focus == 1) stringinput_display (&string); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* dirfilelist */ if (scrollmenu_stdkeys (in_ch, &dirfilelist) >= 0) { oldselected = dirfilelist.selected; i = dirfilemenu_process_select (&dirfilelist, helpstring); if (i == 0) /* filename in helpstring */ { strcpy (string.string, helpstring); focus = 1; string.cursorpos = strlen (string.string); string.firstcharonscreen = 0; } else /* dir in helpstring */ { scrollmenu_delete_menu (&dirfilelist); dirfilemenu (helpstring, &dirfilelist); if (dirfilelist.number == 0) { error_window ( "No permission to read this directory."); header (SIGNPR_INFILE_HEADERTEXT); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = oldselected; } else { strcpy (startdir, helpstring); dirfilelist.firstonscreen = 0; charpointer = strrchr (string.string, '/'); if (charpointer != NULL) strcat (helpstring, charpointer + 1); else strcat (helpstring, string.string); strcpy (string.string, helpstring); } } } else switch (in_ch) { case KEY_LEFT: focus--; break; case KEY_RIGHT: focus++; break; } break; case 1: /* string */ stringinput_stdkeys (in_ch, &string); if (in_ch == KEY_ENTER || in_ch == 13) { strcpy (helpstring, string.string); /* cut away last '/'-s */ while (strlen (helpstring) > 0 && helpstring[strlen (helpstring) - 1] == '/') helpstring[strlen (helpstring) - 1] = '\0'; strcat (helpstring, "/"); if (!stat (helpstring, &filestats) && S_ISDIR (filestats.st_mode)) { strcpy (startdir, helpstring); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.firstonscreen = 0; dirfilelist.selected = dirfilelist.last_of_1st_part + 1; strcpy (string.string, startdir); string.cursorpos = strlen (string.string); focus = 0; } else /* it's a file */ focus = 3; } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 3: /* Next > */ if (in_ch == KEY_ENTER || in_ch == 13) switch (checkfile (string.string)) { case FILE_EXISTS: strcpy (selectedfile, string.string); returnval = 2; dont_stop = FALSE; break; case DIR_EXISTS: error_window ("The specified name is of a directory. A \ file name must be specified."); header (SIGNPR_INFILE_HEADERTEXT); string.cursorpos = strlen (string.string); focus = 1; break; case DIR_OK_NEW_FILE: case DIR_WRONG: error_window ("The specified file does not exist."); header (SIGNPR_INFILE_HEADERTEXT); string.cursorpos = strlen (string.string); focus = 1; break; default: error_window ("Fell out of switch, signpr_infilenm #1"); header (SIGNPR_INFILE_HEADERTEXT); break; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 3) focus = 0; if (focus < 0) focus = 3; } while (dont_stop); scrollmenu_delete_menu (&dirfilelist); free (string.string); return returnval; } gramofile-1.6/signpr_infilenm.h100644 1751 144 767 7070217416 15573 0ustar costarusers/* Signal Processing - Get infile-name - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_INFILENM_H #define HAVE_SIGNPR_INFILENM_H #define SIGNPR_INFILE_HEADERTEXT "Signal Processing - Source File" int signproc_select_infile (char *startdir, char *selectedfile); /* Returns 0: canceled, 2: NextScreen */ #endif /* HAVE_SIGNPR_INFILENM_H */ gramofile-1.6/signpr_main.c100644 1751 144 26127 7070217416 14747 0ustar costarusers/* Signal Processing Main * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_main.h" #include "signpr_general.h" #include "signpr_infilenm.h" #include "signpr_filtmenu.h" #include "signpr_outfilenm.h" #include "errorwindow.h" #include "signpr_wav.h" #include "fmtheaders.h" #include "clrscr.h" #include "secshms.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif int signproc_get_options (char *startdir, char *infilename, char *outfilename, scrollmenu_t * filtlist, int *filtnumbers, char **helptexts, scrollmenu_t * selectedfilts, int *usetracktimes, int *usebeginendtime, double *begintime, double *endtime) { int currscreen = 0; int options_ready = 0; int returnval = 0; /* 0: Cancel, 1: OK */ char oldinfilename[250]; struct stat buf; oldinfilename[0] = '\0'; do switch (currscreen) { case 0: switch (signproc_select_infile (startdir, infilename)) /* 0: Cancel, 1: PreviousScreen, 2: NextScreen/Start */ { case 0: options_ready = 1; returnval = 0; break; case 2: currscreen = 1; break; /* default: currscreen+=0 */ } break; case 1: if (strcmp (infilename, oldinfilename)) { strcpy (oldinfilename, infilename); stat (infilename, &buf); if (buf.st_size < sizeof (wavhead)) *endtime = 0; else *endtime = (buf.st_size - sizeof (wavhead)) / (2 * 2 * 44100.); *begintime = 0; /* *usetracktimes = 1; Is static now. *usebeginendtime = 0; */ } switch (signproc_select_filters (filtlist, filtnumbers, helptexts, selectedfilts, usetracktimes, usebeginendtime, begintime, endtime)) /* 0: Cancel, 1: PreviousScreen, 2: NextScreen/Start */ { case 0: options_ready = 1; returnval = 0; break; case 1: currscreen = 0; break; case 2: currscreen = 2; break; /* default: currscreen+=0 */ } break; case 2: switch (signproc_select_outfile (startdir, outfilename)) /* 0: Cancel, 1: PreviousScreen, 2: NextScreen/Start */ { case 0: options_ready = 1; returnval = 0; break; case 1: currscreen = 1; break; case 2: returnval = 1; options_ready = 1; break; /* default: currscreen+=0 */ } break; } while (!options_ready); return returnval; } long totalsize_samples; long current_total_sample; long tracksize_samples; long current_sample; #define TRACKS_TEXT "[Tracks]" #define NUM_OF_TRACKS_TEXT "Number_of_tracks=" #define notMORE_VERBOSE int load_track_times (char *filename, beginendsample_t * tracktimes, int *number_of_tracks) /* Returns: 0: error, 1: OK */ { char tempstring[250]; char tempstring2[250]; FILE *tracksfile; long tracks_pos; int i; double seconds; strcpy (tempstring, filename); strcat (tempstring, ".tracks"); tracksfile = fopen (tempstring, "r"); if (tracksfile == NULL) return 0; tempstring[0] = '\0'; while (!feof (tracksfile) && strncasecmp (tempstring, TRACKS_TEXT, strlen (TRACKS_TEXT))) fgets (tempstring, 250, tracksfile); if (feof (tracksfile)) /* [Tracks] on last line => stop reading */ { #ifdef MORE_VERBOSE error_window ("[Tracks] not found."); #endif fclose (tracksfile); return 0; } tracks_pos = ftell (tracksfile); tempstring[0] = '\0'; while (!feof (tracksfile) && strncasecmp (tempstring, NUM_OF_TRACKS_TEXT, strlen (NUM_OF_TRACKS_TEXT))) fgets (tempstring, 250, tracksfile); if (strncasecmp (tempstring, NUM_OF_TRACKS_TEXT, strlen (NUM_OF_TRACKS_TEXT))) { #ifdef MORE_VERBOSE error_window ("Number_of_tracks not found."); #endif fclose (tracksfile); return 0; } *number_of_tracks = atoi (tempstring + strlen (NUM_OF_TRACKS_TEXT)); if (*number_of_tracks < 1 || *number_of_tracks > 99) { #ifdef MORE_VERBOSE error_window ("Wrong number or tracks."); #endif fclose (tracksfile); return 0; } for (i = 1; i <= *number_of_tracks; i++) { sprintf (tempstring2, "Track%02dstart=", i); /* Start time */ fseek (tracksfile, tracks_pos, SEEK_SET); tempstring[0] = '\0'; while (!feof (tracksfile) && strncasecmp (tempstring, tempstring2, strlen (tempstring2))) fgets (tempstring, 250, tracksfile); if (strncasecmp (tempstring, tempstring2, strlen (tempstring2))) { #ifdef MORE_VERBOSE error_window ("TrackXXstart not found."); #endif fclose (tracksfile); return 0; } tempstring[strlen (tempstring) - 1] = '\0'; /* Del \n */ if (!hmsf2fsec (tempstring + strlen (tempstring2), &seconds)) { #ifdef MORE_VERBOSE error_window ("Could not convert start time."); #endif fclose (tracksfile); return 0; } tracktimes[i].begin = 44100 * seconds; sprintf (tempstring2, "Track%02dend=", i); /* End time */ fseek (tracksfile, tracks_pos, SEEK_SET); tempstring[0] = '\0'; while (!feof (tracksfile) && strncasecmp (tempstring, tempstring2, strlen (tempstring2))) fgets (tempstring, 250, tracksfile); if (strncasecmp (tempstring, tempstring2, strlen (tempstring2))) { #ifdef MORE_VERBOSE error_window ("TrackXXend not found."); #endif fclose (tracksfile); return 0; } tempstring[strlen (tempstring) - 1] = '\0'; /* Del \n */ if (!hmsf2fsec (tempstring + strlen (tempstring2), &seconds)) { #ifdef MORE_VERBOSE error_window ("Could not convert end time."); #endif fclose (tracksfile); return 0; } tracktimes[i].end = 44100 * seconds; } fclose (tracksfile); return 1; } void signproc_main (char *startdir) { char infilename[250]; char outfilename[250]; char baseoutfilename[250]; char outfileextension[250]; /* The menu settings are made static to allow the selected filters and settings to be remembered between invocations */ static int first_entry = 1; static scrollmenu_t filtlist; static char *filtlist_items[MAX_FILTERS + 10]; static int filtnumbers[MAX_FILTERS + 10]; static char *helptexts[MAX_FILTERS + 10]; static scrollmenu_t selectedfilts; static char *selectedfilts_items[MAX_FILTERS]; static int usebeginendtime = 0, usetracktimes = 1; static double begintime = 0, endtime = 0; struct stat buf; int i; char *charptr; int in_ch; beginendsample_t tracktimes[100]; /* max. 99 tracks: 1 (!) - 99 */ int number_of_tracks; if (first_entry) { filtlist.items = filtlist_items; /* malloc also works :) */ make_filterlist (&filtlist, filtnumbers, helptexts); selectedfilts.items = selectedfilts_items; number_of_filters = 0; selectedfilts.number = 0; /* Default filter: Conditional Median Filter II: ******* NOTE: The exact number must be changed when new filters are added! */ selectedfilts.items[number_of_filters] = filtlist.items[7]; filter_type[number_of_filters] = filtnumbers[7]; parampointerarray[number_of_filters] = (parampointer_t) malloc (sizeof (param_t)); param_defaults (parampointerarray[number_of_filters], filtnumbers[7]); number_of_filters++; selectedfilts.number = number_of_filters; selectedfilts.selected = number_of_filters - 1; /* ----- End (default filter) */ first_entry = 0; } infilename[0] = '\0'; outfilename[0] = '\0'; if (!signproc_get_options (startdir, infilename, outfilename, &filtlist, filtnumbers, helptexts, &selectedfilts, &usetracktimes, &usebeginendtime, &begintime, &endtime)) return; strcpy (baseoutfilename, outfilename); strcpy (outfileextension, ".wav"); /* IF there is a last '.' AND it's after the last '/' THEN make a new basename and extension */ if ((charptr = strrchr (outfilename, '.')) != NULL && strchr (charptr, '/') == NULL ) { baseoutfilename[charptr - outfilename] = '\0'; strcpy (outfileextension, charptr); } if (usebeginendtime) { number_of_tracks = 1; tracktimes[1].begin = begintime * 44100; tracktimes[1].end = endtime * 44100; } else if (usetracktimes) { if (!load_track_times (infilename, tracktimes, &number_of_tracks)) { error_window ("No (correct) track information is available for \ the specified source file."); return; } } else /* entire file */ { number_of_tracks = 1; stat (infilename, &buf); tracktimes[1].begin = 0; tracktimes[1].end = (buf.st_size - sizeof (wavhead)) / (2 * 2); } totalsize_samples = 0; /* calculate sample totals */ current_total_sample = 0; for (i = 1; i <= number_of_tracks; i++) totalsize_samples += tracktimes[i].end - tracktimes[i].begin; if (!openwavsource (infilename)) return; /* open source */ def_prog_mode (); /* save terminal state */ for (i = 1; i <= number_of_tracks; i++) { clearscreen (SIGNPR_PROCESSING_HEADERTEXT); error_window_display ("", " Cancel "); mvprintw (ERROR_WINDOW_Y, ERROR_WINDOW_X + 1, "Track: %2d of %d.", i, number_of_tracks); nodelay (stdscr, TRUE); /* don't wait for a key */ /* calculate #samples for this track */ tracksize_samples = tracktimes[i].end - tracktimes[i].begin + 1; /* seek to beginsample */ if (!seeksamplesource (tracktimes[i].begin)) { reset_prog_mode (); nodelay (stdscr, FALSE); error_window ("The start position of the track could not be \ found. This track will be skipped."); break; } if (number_of_tracks > 1) /* make outfile name */ sprintf (outfilename, "%s%02d%s", baseoutfilename, i, outfileextension); /* open destination file */ if (!openwavdest (outfilename, tracksize_samples * 4)) { reset_prog_mode (); nodelay (stdscr, FALSE); error_window ("The destination file could not be opened. This \ track will be skipped."); break; } init_filters (); /* process the signal */ for (current_sample = 0; current_sample < tracksize_samples; current_sample++) { if (!(current_sample % 4000)) /* progress indicator */ { mvprintw (ERROR_WINDOW_Y + 1, ERROR_WINDOW_X + 1, "Done: %3ld%% track", (long) ((100. * current_sample) / tracksize_samples)); mvprintw (ERROR_WINDOW_Y + 2, ERROR_WINDOW_X + 1, " %3ld%% total", (long) ((100. * current_total_sample) / totalsize_samples)); move (0, 79); refresh (); in_ch = getch (); if (in_ch == 27 || in_ch == 13 || in_ch == KEY_ENTER) { reset_prog_mode (); nodelay (stdscr, FALSE); closewavdest (); closewavsource (); delete_filters (); return; } } /* process one sample */ writesampledest (get_sample_from_filter (number_of_filters - 1)); current_total_sample++; /* update total count */ } closewavdest (); /* close destination */ delete_filters (); /* delete filters */ } closewavsource (); /* close source */ reset_prog_mode (); /* reset terminal state */ nodelay (stdscr, FALSE); } gramofile-1.6/signpr_main.h100644 1751 144 1552 7070217416 14727 0ustar costarusers/* Signal Processing Main - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPROC_MAIN_H #define HAVE_SIGNPROC_MAIN_H #include "scrollmenu.h" #define SIGNPR_PROCESSING_HEADERTEXT "Signal Processing" typedef struct { long begin; long end; } beginendsample_t; int signproc_get_options (char *startdir, char *infilename, char *outfilename, scrollmenu_t * filtlist, int *filtnumbers, char **helptexts, scrollmenu_t * selectedfilts, int *usetracktimes, int *usebeginendtime, double *begintime, double *endtime); int load_track_times (char *filename, beginendsample_t * tracktimes, int *number_of_tracks); void signproc_main (char *startdir); #endif /* HAVE_SIGNPROC_MAIN_H */ gramofile-1.6/signpr_median.c100644 1751 144 13531 7070217416 15253 0ustar costarusers/* Simple Median Filter * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_median.h" #include "signpr_general.h" #include "errorwindow.h" #include "stringinput.h" #include "buttons.h" #include "clrscr.h" #include "boxes.h" #include "helpline.h" #include #include #ifndef OLD_CURSES #include #else #include #endif void simple_median_param_defaults (parampointer_t parampointer) { parampointer->postlength1 = 1; parampointer->prelength1 = 1; } void simple_median_param_screen (parampointer_t parampointer) { stringinput_t medlengthstr; button_t ok_button, cancel_button; int dont_stop = TRUE; int returnval = 0; int focus = 0; int in_ch; int i; long helplong; char *helplines[3] = { " ^: broader ticks removed. v: less distortion. ", " Discard changes. ", " Accept changes. "}; medlengthstr.maxlen = 500; medlengthstr.string = (char *) malloc (medlengthstr.maxlen * sizeof (char)); sprintf (medlengthstr.string, "%ld", parampointer->prelength1 + parampointer->postlength1 + 1); medlengthstr.y = 6; medlengthstr.x = 44; medlengthstr.w = 19; medlengthstr.cursorpos = strlen (medlengthstr.string); medlengthstr.firstcharonscreen = strlen (medlengthstr.string) - medlengthstr.w + 2; if (medlengthstr.firstcharonscreen < 0) medlengthstr.firstcharonscreen = 0; ok_button.text = " OK "; ok_button.y = 20; ok_button.x = 71; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; clearscreen (SIGNPR_MEDIAN_PARAMSCR_HEADERTEXT); do { header (SIGNPR_MEDIAN_PARAMSCR_HEADERTEXT); if (focus == 1) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 2) ok_button.selected = TRUE; else ok_button.selected = FALSE; mvprintw (3, 2, "See the Signproc.txt file for the meaning of the parameters."); stringinput_display (&medlengthstr); mvprintw (medlengthstr.y, 2, "Number of samples to take the median of:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); helpline (helplines[focus]); if (focus == 0) stringinput_display (&medlengthstr); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* medlengthstr */ stringinput_stdkeys (in_ch, &medlengthstr); if (in_ch == KEY_ENTER || in_ch == 13) { i = sscanf (medlengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus = 2; } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 1: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 2: /* OK */ if (in_ch == KEY_ENTER || in_ch == 13) { i = sscanf (medlengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as median length."); medlengthstr.cursorpos = strlen (medlengthstr.string); focus = 0; } else { parampointer->prelength1 = (helplong - 1) / 2; parampointer->postlength1 = (helplong - 1) / 2; dont_stop = FALSE; } } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 2) focus -= 3; if (focus < 0) focus += 3; } while (dont_stop); free (medlengthstr.string); } void init_simple_median_filter (int filterno, parampointer_t parampointer) { parampointer->buffer = init_buffer (parampointer->postlength1, parampointer->prelength1); parampointer->filterno = filterno; parampointer->sslist1 = (signed short *) malloc ((parampointer->postlength1 + parampointer->prelength1 + 1) * sizeof (signed short)); parampointer->sslist2 = (signed short *) malloc ((parampointer->postlength1 + parampointer->prelength1 + 1) * sizeof (signed short)); } void delete_simple_median_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); free (parampointer->sslist1); free (parampointer->sslist2); } sample_t simple_median_filter (parampointer_t parampointer) { sample_t sample; long i; advance_current_pos (¶mpointer->buffer, parampointer->filterno); for (i = 0; i <= parampointer->postlength1 + parampointer->prelength1; i++) { sample = get_from_buffer (¶mpointer->buffer, i - parampointer->postlength1); parampointer->sslist1[i] = sample.left; parampointer->sslist2[i] = sample.right; } sample.left = median (parampointer->sslist1, parampointer->postlength1 + parampointer->prelength1 + 1); sample.right = median (parampointer->sslist2, parampointer->postlength1 + parampointer->prelength1 + 1); return sample; } gramofile-1.6/signpr_median.h100644 1751 144 1355 7070217416 15241 0ustar costarusers/* Simple Median Filter - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_MEDIAN_H #define HAVE_SIGNPR_MEDIAN_H #include "signpr_general.h" #define SIGNPR_MEDIAN_PARAMSCR_HEADERTEXT "Simple Median filter - Parameters" void simple_median_param_defaults (parampointer_t parampointer); void simple_median_param_screen (parampointer_t parampointer); void init_simple_median_filter (int filterno, parampointer_t parampointer); void delete_simple_median_filter (parampointer_t parampointer); sample_t simple_median_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_MEDIAN_H */ gramofile-1.6/signpr_outfilenm.c100644 1751 144 21004 7070217416 16012 0ustar costarusers/* Signal Processing - Get outfile-name * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_outfilenm.h" #include "scrollmenu.h" #include "stringinput.h" #include "buttons.h" #include "boxes.h" #include "dirfilemenu.h" #include "errorwindow.h" #include "textwindow.h" #include "checkfile.h" #include "yesnowindow.h" #include "helpline.h" #include "clrscr.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif int signproc_select_outfile (char *startdir, char *selectedfile) /* Returns 0: canceled, 1: PrevScreen, 2: NextScreen */ { scrollmenu_t dirfilelist; stringinput_t string; button_t next_button, cancel_button, prev_button; int dont_stop = TRUE; int returnval = 0; int focus = 1; int in_ch; int i; char helpstring[500]; char *charpointer; struct stat filestats; int oldselected; char *helplines[5] = { " Browse files and directories. TAB: Next field", " Enter name of destination sound file. TAB: Next field", " To Signal Processing - Filter Selection. TAB: Next field", " Back to main menu. TAB: Next field", " Start processing the signal. TAB: Next field"}; dirfilelist.y = 3; dirfilelist.x = 5; dirfilelist.h = 12; dirfilelist.w = 32; dirfilelist.firstonscreen = 0; dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = dirfilelist.last_of_1st_part + 1; string.maxlen = 500; string.string = (char *) malloc (string.maxlen * sizeof (char)); if (selectedfile[0] == '\0') { strcpy (string.string, startdir); strcat (string.string, "processed.wav"); } else strcpy (string.string, selectedfile); string.y = 17; string.x = 5; string.w = 70; string.cursorpos = strlen (string.string); string.firstcharonscreen = strlen (string.string) - string.w + 2; if (string.firstcharonscreen < 0) string.firstcharonscreen = 0; prev_button.text = " < Previous screen "; prev_button.y = 20; prev_button.x = 5; prev_button.selected = FALSE; next_button.text = " Start "; next_button.y = 20; next_button.x = 68; next_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 36; cancel_button.selected = FALSE; clearscreen (SIGNPR_OUTFILE_HEADERTEXT); do { if (focus == 2) prev_button.selected = TRUE; else prev_button.selected = FALSE; if (focus == 3) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 4) next_button.selected = TRUE; else next_button.selected = FALSE; dirfilelist.hasfocus = (focus == 0); scrollmenu_display (&dirfilelist); mybox (dirfilelist.y - 1, dirfilelist.x - 1, dirfilelist.h + 2, dirfilelist.w + 2); mvprintw (dirfilelist.y - 1, dirfilelist.x + 1, "Files and directories:"); stringinput_display (&string); mybox (string.y - 1, string.x - 1, 3, string.w + 2); mvprintw (string.y - 1, string.x + 1, "Destination File Name:"); button_display (&prev_button); mybox (prev_button.y - 1, prev_button.x - 1, 3, strlen (prev_button.text) + 2); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&next_button); mybox (next_button.y - 1, next_button.x - 1, 3, strlen (next_button.text) + 2); helpline (helplines[focus]); if (focus == 1) stringinput_display (&string); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* dirfilelist */ if (scrollmenu_stdkeys (in_ch, &dirfilelist) >= 0) { oldselected = dirfilelist.selected; i = dirfilemenu_process_select (&dirfilelist, helpstring); if (i == 0) /* filename in helpstring */ { strcpy (string.string, helpstring); focus = 1; string.cursorpos = strlen (string.string); string.firstcharonscreen = 0; } else /* dir in helpstring */ { scrollmenu_delete_menu (&dirfilelist); dirfilemenu (helpstring, &dirfilelist); if (dirfilelist.number == 0) { error_window ( "No permission to read this directory."); header (SIGNPR_OUTFILE_HEADERTEXT); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = oldselected; } else { strcpy (startdir, helpstring); dirfilelist.firstonscreen = 0; charpointer = strrchr (string.string, '/'); if (charpointer != NULL) strcat (helpstring, charpointer + 1); else strcat (helpstring, string.string); strcpy (string.string, helpstring); } } } else switch (in_ch) { case KEY_LEFT: focus--; break; case KEY_RIGHT: focus++; break; } break; case 1: /* string */ stringinput_stdkeys (in_ch, &string); if (in_ch == KEY_ENTER || in_ch == 13) { strcpy (helpstring, string.string); /* cut away last '/'-s */ while (strlen (helpstring) > 0 && helpstring[strlen (helpstring) - 1] == '/') helpstring[strlen (helpstring) - 1] = '\0'; strcat (helpstring, "/"); if (!stat (helpstring, &filestats) && S_ISDIR (filestats.st_mode)) { strcpy (startdir, helpstring); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.firstonscreen = 0; dirfilelist.selected = dirfilelist.last_of_1st_part + 1; strcpy (string.string, startdir); string.cursorpos = strlen (string.string); focus = 0; } else /* it's a file */ focus = 4; } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* < Previous */ if (in_ch == KEY_ENTER || in_ch == 13) { strcpy (selectedfile, string.string); returnval = 1; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 3: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 4: /* Next > */ if (in_ch == KEY_ENTER || in_ch == 13) switch (checkfile (string.string)) { case FILE_EXISTS: if (yesno_window ("The specified file already exists. \ Overwrite it?", " Yes ", " No ", 0)) { strcpy (selectedfile, string.string); returnval = 2; dont_stop = FALSE; } else { string.cursorpos = strlen (string.string); focus = 1; header (SIGNPR_OUTFILE_HEADERTEXT); } break; case DIR_EXISTS: error_window ("The specified name is of a directory. A \ file name must be specified."); header (SIGNPR_OUTFILE_HEADERTEXT); string.cursorpos = strlen (string.string); focus = 1; break; case DIR_OK_NEW_FILE: strcpy (selectedfile, string.string); returnval = 2; dont_stop = FALSE; break; case DIR_WRONG: if (*(string.string) == '|') { /* Piping to a command, so expect `dir' not to exist */ strcpy (selectedfile, string.string); returnval = 2; dont_stop = FALSE; } else { error_window ("The directory of the specified file does \ not exist."); header (SIGNPR_OUTFILE_HEADERTEXT); string.cursorpos = strlen (string.string); focus = 1; } break; default: error_window ("Fell out of switch, signpr_outfilenm #1"); header (SIGNPR_OUTFILE_HEADERTEXT); break; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 4) focus = 0; if (focus < 0) focus = 4; } while (dont_stop); scrollmenu_delete_menu (&dirfilelist); free (string.string); return returnval; } gramofile-1.6/signpr_outfilenm.h100644 1751 144 1021 7070217416 15774 0ustar costarusers/* Signal Processing - Get outfile-name - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_OUTFILENM_H #define HAVE_SIGNPR_OUTFILENM_H #define SIGNPR_OUTFILE_HEADERTEXT "Signal Processing - Destination File" int signproc_select_outfile (char *startdir, char *selectedfile); /* Returns 0: canceled, 1: PrevScreen, 2: NextScreen */ #endif /* HAVE_SIGNPR_OUTFILENM_H */ gramofile-1.6/signpr_wav.c100644 1751 144 12550 7070217416 14613 0ustar costarusers/* Signal Processing - Wave File Functions * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_wav.h" #include "fmtheaders.h" #include "errorwindow.h" #include "signpr_general.h" #include #include #include #include #include #include #include "endian.h" /* ----- SOURCE & READING -------------------------------------------------- */ FILE *sourcefile; int openwavsource (char *filename) /* returns 0: failure (error_window has been displayed), 1: success. More or less adapted from bplay.c, with stdio-patch by Axel Kohlmeyer */ { int count; char hd_buf[20]; wavhead wavhd; if ((sourcefile = fopen (filename, "rb")) == NULL) { error_window ("The source file could not be opened."); return 0; } count = fread (hd_buf, 1, 20, sourcefile); if (count < 20) { fclose (sourcefile); error_window ("The source file could not be read, or is too short."); return 0; } if (strstr (hd_buf, "RIFF") == NULL) { fclose (sourcefile); error_window ("The source file is not a .wav file, and can not be \ processed."); return 0; } memcpy ((void *) &wavhd, (void *) hd_buf, 20); count = fread (((char *) &wavhd) + 20, 1, sizeof (wavhd) - 20, sourcefile); if (count < sizeof (wavhd) - 20) { fclose (sourcefile); error_window ("The source file is too short. Probably it is not \ a .wav sound file."); return 0; } #ifdef SWAP_ENDIAN wavhd.format = SwapTwoBytes (wavhd.format); wavhd.sample_fq = SwapFourBytes (wavhd.sample_fq); wavhd.bit_p_spl = SwapTwoBytes (wavhd.bit_p_spl); wavhd.modus = SwapTwoBytes (wavhd.modus); #endif if (wavhd.format != 1) { fclose (sourcefile); error_window ("The source file is a .wav file with unknown format, \ and can not be processed."); return 0; } if (wavhd.sample_fq != 44100) { fclose (sourcefile); error_window ("The source file is not sampled at 44100 Hz, and can \ not be processed."); return 0; } if (wavhd.bit_p_spl != 16) { fclose (sourcefile); error_window ("The source file does not have 16 bit samples, and \ can not be processed."); return 0; } if (wavhd.modus != 2) { fclose (sourcefile); error_window ("The source file is not stereo, and can not be \ processed."); return 0; } /* Well, everything seems to be OK */ return 1; } void closewavsource () { fclose (sourcefile); } int seeksamplesource (long samplenumber) /* returns 0: failure, 1: success */ { struct stat buf; if (fstat (fileno (sourcefile), &buf)) return 0; if (sizeof (wavhead) + 2 * 2 * samplenumber >= buf.st_size) return 0; if (fseek (sourcefile, sizeof (wavhead) + 2 * 2 * samplenumber, SEEK_SET) > -1) return 1; else return 0; } sample_t readsamplesource () { sample_t sample; if (fread (&sample, 1, 4, sourcefile) != 4) { /* reading after end of file - this just happens when using pre-read buffers! */ sample.left = 0; sample.right = 0; } #ifdef SWAP_ENDIAN sample = SwapSample (sample); #endif return sample; } /* ----- DESTINATION & WRITING --------------------------------------------- */ FILE *destfile; int destfileispipe; /* remember open() - - -> close() */ int openwavdest (char *filename, long bcount) /* returns 0: failure (error_window has NOT been displayed), 1: success. bcount must be 4 * number_of_samples ! Adapted from bplay.c */ { wavhead header; char *riff = "RIFF"; char *wave = "WAVE"; char *fmt = "fmt "; char *data = "data"; if (*filename == '|') { destfileispipe = 1; /* remember for closing */ if ((destfile = popen (filename + 1, "w")) == NULL) return 0; } else { destfileispipe = 0; if ((destfile = fopen (filename, "wb")) == NULL) return 0; } 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 + 1; header.sample_fq = /*speed */ 44100; header.byte_p_sec = /*((bits > 8)? 2:1)*(stereo+1)*speed */ 2 * 2 * 44100; header.byte_p_spl = /*(bits > 8)? 2:1 */ 4; /* stereo & 16 bit..? */ header.bit_p_spl = /*bits */ 16; /* stereo doesn't count here? */ memcpy (&(header.data_chunk), data, 4); header.data_length = bcount; #ifdef SWAP_ENDIAN header.length = SwapFourBytes (header.length); header.sc_len = SwapFourBytes (header.sc_len); header.format = SwapTwoBytes (header.format); header.modus = SwapTwoBytes (header.modus); header.sample_fq = SwapFourBytes (header.sample_fq); header.byte_p_sec = SwapFourBytes (header.byte_p_sec); header.byte_p_spl = SwapTwoBytes (header.byte_p_spl); header.bit_p_spl = SwapTwoBytes (header.bit_p_spl); header.data_length = SwapFourBytes (header.data_length); #endif fwrite (&header, sizeof (header), 1, destfile); return 1; } void closewavdest () { if (destfileispipe) pclose (destfile); else fclose (destfile); } void writesampledest (sample_t sample) { #ifdef SWAP_ENDIAN sample = SwapSample (sample); #endif fwrite (&sample, 4, 1, destfile); } gramofile-1.6/TODO100644 1751 144 7301 7067753675 12760 0ustar costarusers Things that could/should be done (random order) - but not by me: I've got other things to do at the moment :( - Find and correct all bugs - Better error handling (malloc, file ops) - Support (user interface) for cdrecord, cdda2wav/cdparanoia - More / better filters - pops/ticks/scratches - hiss, broadband noise - fade in/out at track start/end (see below) - volume normalizing to put tracks from various sources on one CD - dynamic-range decompression (Keith Refson's idea) - Possibility for application of frequency domain filters (probably difficult to do in streaming processing) - Remember filter/track location settings within a single invocation (Also see patch from James Tappin on the webpage) - Make .wav reading/writing 64-bit clean (i.e. don't depend on sizeof's) (temporary workaround: change every `long' into `int', see README) - Compute average/maximal signal volume/power for info-screen after recording (sum of 1G shorts in a float) (see bplaysrc/shmbuf.c, shmrec()) - Show number of nearly-clipped samples also _during_ recording, for easier adjustments of sound source (idea of Juergen Lock) - Copying, moving, deleting files; creating, deleting dirs from within user interface (?) - Automatic detection which of xmixer, xmix, aumix, ???mix to use (e.g. $DISPLAY ? or $MIXER ?) - Auto screensize (not always 80x24) - X user interface - Manpage - Support for non-CD-quality and non-.wav sound files; (for mono files, also see patch from James Tappin on the webpage) - Command line options (or via a options file) - Porting to other *UXes, Windows? First only Signproc/Tracksplit (IRIX and possibly others should work already. If you try, please mail me your experiences) - For webpage: example .wavs, example graph files (track location), screenshots of (un)processed .wavs Fade in/out: Paul Martin sent me the following: -----quote----- [...] Here's a fragment of code that does a mono fade out. It's very sub-optimal, but it does work. It implements a (1-x^2) fade (x = 0 -> 1). By changing the exponent you can change the type of fade from linear (x^1) to highly logarithmic. [...] #include #include #include #include #include #include #include const char inname[]="input.sw"; const char outname[]="output.sw"; signed short buff[441000]; main () { double frac,scaler; long i,size,max; int inp,oup; signed short tmp; if ((inp=open(inname,O_RDONLY))==-1) { perror("open('input.sw')"); exit(1); } size = read(inp, buff, sizeof(buff)); close(inp); max = (size/2); for (i=0; i #else #include #endif void stringinput_display (stringinput_t * data) { int i; if (data->cursorpos < 0) data->cursorpos = 0; if (data->cursorpos > strlen (data->string)) data->cursorpos = strlen (data->string); if (data->cursorpos < data->firstcharonscreen + 2) data->firstcharonscreen = data->cursorpos - 2; if (data->cursorpos >= data->firstcharonscreen + data->w) data->firstcharonscreen = data->cursorpos - data->w + 1; if (data->firstcharonscreen < 0) data->firstcharonscreen = 0; move (data->y, data->x); i = data->firstcharonscreen; while (i < strlen (data->string) && i < data->firstcharonscreen + data->w) { addch (data->string[i]); i++; } while (i < data->firstcharonscreen + data->w) { addch (' '); i++; } move (data->y, data->x + data->cursorpos - data->firstcharonscreen); } void stringinput_stdkeys (int key, stringinput_t * data) { char helpstring[500]; switch (key) { case KEY_LEFT: data->cursorpos--; break; case KEY_RIGHT: data->cursorpos++; break; case KEY_HOME: data->cursorpos = 0; break; case KEY_END: data->cursorpos = strlen (data->string); break; case KEY_BACKSPACE: #ifdef TREAT_DEL_AS_BACKSPACE case 127: #endif if (data->cursorpos > 0) { strcpy (helpstring, data->string); strcpy (helpstring + data->cursorpos - 1, data->string + data->cursorpos); strcpy (data->string, helpstring); data->cursorpos--; } break; #ifndef TREAT_DEL_AS_BACKSPACE case 127: /* DEL */ if (data->cursorpos < strlen (data->string)) { strcpy (helpstring, data->string); strcpy (helpstring + data->cursorpos, data->string + data->cursorpos + 1); strcpy (data->string, helpstring); } break; #endif } if (key >= 32 && key <= 126) /* insert */ { strcpy (helpstring, data->string); strcpy (helpstring + data->cursorpos + 1, data->string + data->cursorpos); helpstring[data->cursorpos] = (char) key; strcpy (data->string, helpstring); data->cursorpos++; } if (data->cursorpos < 0) data->cursorpos = 0; if (data->cursorpos > strlen (data->string)) data->cursorpos = strlen (data->string); } gramofile-1.6/stringinput.h100644 1751 144 1131 7070217416 15000 0ustar costarusers/* String input - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_STRINGINPUT_H #define HAVE_STRINGINPUT_H #define TREAT_DEL_AS_BACKSPACE typedef struct { char *string; int y; int x; int w; int maxlen; int cursorpos; int firstcharonscreen; } stringinput_t; extern void stringinput_display (stringinput_t * data); extern void stringinput_stdkeys (int key, stringinput_t * data); #endif /* HAVE_STRINGINPUT_H */ gramofile-1.6/textwindow.c100644 1751 144 2666 7070217416 14637 0ustar costarusers/* Text in a 'window' * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "textwindow.h" #include #include #ifndef OLD_CURSES #include #else #include #endif void display_textwin (char *text, int y, int x, int h, int w) /* (y,x): upper left position of 'text block' h = heigth, w = width of block */ { int current_y; char mytext[DISPLAYMENU_MAXTEXTLEN]; char mytext2[DISPLAYMENU_MAXTEXTLEN]; char helptext[DISPLAYMENU_MAXTEXTLEN]; char *lastspace; int i; current_y = y; strncpy (mytext, text, DISPLAYMENU_MAXTEXTLEN); do { strncpy (mytext2, mytext, w + 1); mytext2[w + 1] = '\0'; if (strlen (mytext2) > w) { lastspace = strrchr (mytext2, ' '); if (lastspace != NULL) { mytext2[lastspace - mytext2] = '\0'; strcpy (helptext, mytext + (lastspace - mytext2 + 1)); strcpy (mytext, helptext); } else /* no space... */ { mytext2[w] = '\0'; strcpy (helptext, mytext + w); strcpy (mytext, helptext); } } else /* strlen(mytext2) <= w */ mytext[0] = '\0'; mvaddstr (current_y, x, mytext2); for (i = strlen (mytext2) + 1; i <= w; i++) addch (' '); current_y++; } while (current_y < y + h /*&& strlen(mytext) > 0 */ ); } gramofile-1.6/textwindow.h100644 1751 144 756 7070217416 14622 0ustar costarusers/* Text in a 'window' - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_TEXTWINDOW_H #define HAVE_TEXTWINDOW_H #define DISPLAYMENU_MAXTEXTLEN 800 extern void display_textwin (char *text, int y, int x, int h, int w); /* (y,x): upper left position of 'text block' h = heigth, w = width of block */ #endif /* HAVE_TEXTWINDOW_H */ gramofile-1.6/yesnowindow.c100644 1751 144 3633 7070217416 15003 0ustar costarusers/* Yes/No window * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "yesnowindow.h" #include "buttons.h" #include "boxes.h" #include "textwindow.h" #ifndef OLD_CURSES #include #else #include #endif int yesno_window (char *text, char *yestext, char *notext, int preselected) /* returns 1 if yes-button selected, 0 if no-button selected or Escape pressed */ { button_t yes_button, no_button; int i; int focus; yes_button.text = yestext; yes_button.y = YESNO_WINDOW_Y + YESNO_WINDOW_H - 1; yes_button.x = YESNO_WINDOW_X + YESNO_WINDOW_W - 1 - strlen (yes_button.text); yes_button.selected = FALSE; no_button.text = notext; no_button.y = YESNO_WINDOW_Y + YESNO_WINDOW_H - 1; no_button.x = YESNO_WINDOW_X + 1; no_button.selected = FALSE; mybox (YESNO_WINDOW_Y - 1, YESNO_WINDOW_X - 1, YESNO_WINDOW_H + 2, YESNO_WINDOW_W + 2); display_textwin ("", YESNO_WINDOW_Y, YESNO_WINDOW_X, YESNO_WINDOW_H, YESNO_WINDOW_W); display_textwin (text, YESNO_WINDOW_Y, YESNO_WINDOW_X + 1, YESNO_WINDOW_H, YESNO_WINDOW_W - 2); focus = preselected; do { yes_button.selected = FALSE; no_button.selected = FALSE; if (focus == 0) no_button.selected = TRUE; if (focus == 1) yes_button.selected = TRUE; button_display (&yes_button); button_display (&no_button); move (0, 79); refresh (); do i = getch (); while (i != 13 && i != KEY_ENTER && i != 27 && i != KEY_LEFT && i != KEY_RIGHT && i != 9); switch (i) { case KEY_LEFT: focus = 0; break; case KEY_RIGHT: focus = 1; break; case 9: focus = 1 - focus; break; } } while (i != 13 && i != KEY_ENTER && i != 27); clear (); refresh (); if (i == 27) return 0; else return focus; } gramofile-1.6/yesnowindow.h100644 1751 144 1173 7070217416 15005 0ustar costarusers/* Yes/No window - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_YESNOWINDOW_H #define HAVE_YESNOWINDOW_H #define YESNO_WINDOW_Y 9 #define YESNO_WINDOW_X 20 #define YESNO_WINDOW_H 5 #define YESNO_WINDOW_W 40 extern int yesno_window (char *text, char *yestext, char *notext, int preselected); /* preselected = 1: yes-button selected, 0: no-button selected. returns 1 if yes-button selected, 0 if no-button selected or Escape pressed */ #endif /* HAVE_YESNOWINDOW_H */ gramofile-1.6/signpr_mean.c100644 1751 144 12525 7070217416 14740 0ustar costarusers/* Simple Mean Filter * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_mean.h" #include "signpr_general.h" #include "errorwindow.h" #include "stringinput.h" #include "buttons.h" #include "clrscr.h" #include "boxes.h" #include "helpline.h" #include #include #ifndef OLD_CURSES #include #else #include #endif void simple_mean_param_defaults (parampointer_t parampointer) { parampointer->postlength1 = 1; parampointer->prelength1 = 1; } void simple_mean_param_screen (parampointer_t parampointer) { stringinput_t meanlengthstr; button_t ok_button, cancel_button; int dont_stop = TRUE; int returnval = 0; int focus = 0; int in_ch; int i; long helplong; char *helplines[3] = { " ^: more distortion. v: less effective. ", " Discard changes. ", " Accept changes. "}; meanlengthstr.maxlen = 500; meanlengthstr.string = (char *) malloc (meanlengthstr.maxlen * sizeof (char)); sprintf (meanlengthstr.string, "%ld", parampointer->prelength1 + parampointer->postlength1 + 1); meanlengthstr.y = 6; meanlengthstr.x = 38; meanlengthstr.w = 15; meanlengthstr.cursorpos = strlen (meanlengthstr.string); meanlengthstr.firstcharonscreen = 0; ok_button.text = " OK "; ok_button.y = 20; ok_button.x = 71; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; clearscreen (SIGNPR_MEAN_PARAMSCR_HEADERTEXT); do { header (SIGNPR_MEAN_PARAMSCR_HEADERTEXT); if (focus == 1) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 2) ok_button.selected = TRUE; else ok_button.selected = FALSE; mvprintw (3, 2, "See the Signproc.txt file for the meaning of the parameters."); stringinput_display (&meanlengthstr); mvprintw (meanlengthstr.y, 2, "Number of samples to take mean of:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); helpline (helplines[focus]); if (focus == 0) stringinput_display (&meanlengthstr); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* meanlengthstr */ stringinput_stdkeys (in_ch, &meanlengthstr); if (in_ch == KEY_ENTER || in_ch == 13) { i = sscanf (meanlengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus = 2; } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 1: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 2: /* OK */ if (in_ch == KEY_ENTER || in_ch == 13) { i = sscanf (meanlengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as mean length."); meanlengthstr.cursorpos = strlen (meanlengthstr.string); focus = 0; } else { parampointer->prelength1 = (helplong - 1) / 2; parampointer->postlength1 = (helplong - 1) / 2; dont_stop = FALSE; } } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 2) focus -= 3; if (focus < 0) focus += 3; } while (dont_stop); free (meanlengthstr.string); } void init_simple_mean_filter (int filterno, parampointer_t parampointer) { parampointer->buffer = init_buffer (parampointer->postlength1, parampointer->prelength1); parampointer->filterno = filterno; } void delete_simple_mean_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); } sample_t simple_mean_filter (parampointer_t parampointer) { longsample_t sum; sample_t sample; long i; advance_current_pos (¶mpointer->buffer, parampointer->filterno); sum.left = 0; sum.right = 0; for (i = 0; i <= parampointer->postlength1 + parampointer->prelength1; i++) { sample = get_from_buffer (¶mpointer->buffer, i - parampointer->postlength1); sum.left += sample.left; sum.right += sample.right; } sample.left = sum.left / (parampointer->postlength1 + parampointer->prelength1 + 1); sample.right = sum.right / (parampointer->postlength1 + parampointer->prelength1 + 1); return sample; } gramofile-1.6/signpr_mean.h100644 1751 144 1327 7070217416 14723 0ustar costarusers/* Simple Mean Filter - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_MEAN_H #define HAVE_SIGNPR_MEAN_H #include "signpr_general.h" #define SIGNPR_MEAN_PARAMSCR_HEADERTEXT "Simple Mean Filter - Parameters" void simple_mean_param_defaults (parampointer_t parampointer); void simple_mean_param_screen (parampointer_t parampointer); void init_simple_mean_filter (int filterno, parampointer_t parampointer); void delete_simple_mean_filter (parampointer_t parampointer); sample_t simple_mean_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_MEAN_H */ gramofile-1.6/signpr_doubmed.c100644 1751 144 21342 7070217415 15433 0ustar costarusers/* Double Median Filter * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_doubmed.h" #include "signpr_general.h" #include "errorwindow.h" #include "stringinput.h" #include "buttons.h" #include "clrscr.h" #include "boxes.h" #include "helpline.h" #include #include #ifndef OLD_CURSES #include #else #include #endif void double_median_param_defaults (parampointer_t parampointer) { parampointer->postlength1 = 2; parampointer->prelength1 = 2; parampointer->postlength2 = 2; parampointer->prelength2 = 2; } void double_median_param_screen (parampointer_t parampointer) { stringinput_t medlength1str, medlength2str; button_t ok_button, cancel_button; int dont_stop = TRUE; int returnval = 0; int focus = 0; int in_ch; int i; long helplong; char *helplines[4] = { " ^: more distortion. v: less effective against ticks. ", " ^: less correction of distortion. v: more (faulty) correction. ", " Discard changes. ", " Accept changes. "}; medlength1str.maxlen = 500; medlength1str.string = (char *) malloc (medlength1str.maxlen * sizeof (char)); sprintf (medlength1str.string, "%ld", parampointer->prelength1 + parampointer->postlength1 + 1); medlength1str.y = 6; medlength1str.x = 57; medlength1str.w = 19; medlength1str.cursorpos = strlen (medlength1str.string); medlength1str.firstcharonscreen = 0; medlength2str.maxlen = 500; medlength2str.string = (char *) malloc (medlength2str.maxlen * sizeof (char)); sprintf (medlength2str.string, "%ld", parampointer->prelength2 + parampointer->postlength2 + 1); medlength2str.y = 8; medlength2str.x = 57; medlength2str.w = 19; medlength2str.cursorpos = strlen (medlength2str.string); medlength2str.firstcharonscreen = 0; ok_button.text = " OK "; ok_button.y = 20; ok_button.x = 71; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; clearscreen (SIGNPR_DOUBMED_PARAMSCR_HEADERTEXT); do { header (SIGNPR_DOUBMED_PARAMSCR_HEADERTEXT); if (focus == 2) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 3) ok_button.selected = TRUE; else ok_button.selected = FALSE; mvprintw (3, 2, "See the Signproc.txt file for the meaning of the parameters."); stringinput_display (&medlength1str); mvprintw (medlength1str.y, 2, "Number of samples for the first median:"); stringinput_display (&medlength2str); mvprintw (medlength2str.y, 2, "Number of samples for the second (correction) median:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); helpline (helplines[focus]); switch (focus) { case 0: stringinput_display (&medlength1str); break; case 1: stringinput_display (&medlength2str); break; default: move (0, 79); } refresh (); in_ch = getch (); switch (focus) { case 0: /* medlength1str */ stringinput_stdkeys (in_ch, &medlength1str); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (medlength1str.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus = 1; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 1: /* medlength2str */ stringinput_stdkeys (in_ch, &medlength2str); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (medlength2str.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus = 3; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* Cancel */ switch (in_ch) { case KEY_ENTER: case 13: returnval = 0; dont_stop = FALSE; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 3: /* OK */ switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (medlength1str.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as first median length."); medlength1str.cursorpos = strlen (medlength1str.string); focus = 0; break; } parampointer->prelength1 = (helplong - 1) / 2; parampointer->postlength1 = (helplong - 1) / 2; i = sscanf (medlength2str.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as second median length."); medlength2str.cursorpos = strlen (medlength2str.string); focus = 1; break; } parampointer->prelength2 = (helplong - 1) / 2; parampointer->postlength2 = (helplong - 1) / 2; dont_stop = FALSE; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 3) focus = 0; if (focus < 0) focus = 3; } while (dont_stop); free (medlength1str.string); free (medlength2str.string); } void init_double_median_filter (int filterno, parampointer_t parampointer) { long total_post; total_post = parampointer->postlength2; if (parampointer->postlength1 > total_post) total_post = parampointer->postlength1; parampointer->buffer = init_buffer (total_post, parampointer->prelength1 + parampointer->prelength2); parampointer->buffer2 = init_buffer (parampointer->postlength2, parampointer->prelength2); parampointer->filterno = filterno; } void delete_double_median_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); delete_buffer (¶mpointer->buffer2); } sample_t double_median_1 (long offset, long offset_zero, parampointer_t parampointer) { sample_t sample; signed short list1[parampointer->postlength1 + parampointer->prelength1 + 1]; signed short list2[parampointer->postlength1 + parampointer->prelength1 + 1]; long i; for (i = 0; i <= parampointer->postlength1 + parampointer->prelength1; i++) { sample = get_from_buffer (¶mpointer->buffer, i - parampointer->postlength1 + offset + offset_zero); list1[i] = sample.left; list2[i] = sample.right; } sample.left = median (list1, parampointer->postlength1 + parampointer->prelength1 + 1); sample.right = median (list2, parampointer->postlength1 + parampointer->prelength1 + 1); return sample; } fillfuncpointer_t double_median_1_pointer = double_median_1; sample_t double_median_filter (parampointer_t parampointer) { sample_t sample; sample_t sample2; sample_t returnval; signed short list1[parampointer->postlength2 + parampointer->prelength2 + 1]; signed short list2[parampointer->postlength2 + parampointer->prelength2 + 1]; long i, j; advance_current_pos (¶mpointer->buffer, parampointer->filterno); advance_current_pos_custom (¶mpointer->buffer2, double_median_1_pointer, 0, parampointer); for (i = 0; i <= parampointer->postlength2 + parampointer->prelength2; i++) { sample = get_from_buffer (¶mpointer->buffer, i - parampointer->postlength2); sample2 = get_from_buffer (¶mpointer->buffer2, i - parampointer->postlength2); j = sample.left - sample2.left; j /= 2; list1[i] = j; j = sample.right - sample2.right; j /= 2; list2[i] = j; } sample2 = get_from_buffer (¶mpointer->buffer2, 0); returnval.left = median (list1, parampointer->postlength2 + parampointer->prelength2 + 1) * 2 + sample2.left; returnval.right = median (list2, parampointer->postlength2 + parampointer->prelength2 + 1) * 2 + sample2.right; return returnval; } gramofile-1.6/signpr_doubmed.h100644 1751 144 1361 7070217416 15420 0ustar costarusers/* Double Median Filter - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_DOUBMED_H #define HAVE_SIGNPR_DOUBMED_H #include "signpr_general.h" #define SIGNPR_DOUBMED_PARAMSCR_HEADERTEXT "Double Median Filter - Parameters" void double_median_param_defaults (parampointer_t parampointer); void double_median_param_screen (parampointer_t parampointer); void init_double_median_filter (int filterno, parampointer_t parampointer); void delete_double_median_filter (parampointer_t parampointer); sample_t double_median_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_DOUBMED_H */ gramofile-1.6/splashscr.c100644 1751 144 3276 7070217416 14423 0ustar costarusers/* Splash Screen * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include #ifndef OLD_CURSES #include #else #include #endif void splashscreen (void) { char splashtext[] = "\ \n\ ####### ####### ## ###\n\ ## ## ##\n\ ## ## #### ##### ## ### ### ##### ## ### ## #####\n\ ## #### ## ## ## ## ## ## ## ## ##### ## ## ## ##\n\ ## ## ## ###### ## ## ## ## ## ## ## ## ######\n\ ## ## ## ## ## ## ## ## ## ## ## ## ## ##\n\ ####### ## ###### ## ## ## ##### ## #### #### #####\n\ \n\ Version 1.6\n\ ________________________________________________________________________________ \n\ Recording - Playback - Track Splitting - Signal Processing\n\ \n\ \n\ \n\ Copyright (C) 1998 J.A. Bezemer\n\ \n\ This program is free software; you are encouraged to redistribute it under\n\ the terms of the GNU General Public License.\n\ \n\ This program comes with ABSOLUTELY NO WARRANTY. See the GNU General Public\n\ License (e.g. in the file named `COPYING') for more details.\ "; /* int i; for (i=0; i #include #include #include #ifndef OLD_CURSES #include #else #include #endif int tracksplit_get_options (char *startdir, char *filename, int *make_use_rms, int *make_graphs, long *blocklen, int *global_silence_factor, int *local_silence_threshold, int *min_silence_blocks, int *min_track_blocks, int *extra_blocks_start, int *extra_blocks_end) /* Returns 0: Cancel, 1: OK */ { int currscreen = 0; int options_ready = 0; int returnval = 0; /* 0: Cancel, 1: OK */ do switch (currscreen) { case 0: switch (tracksplit_select_file (startdir, filename)) /* 0: Cancel, 1: PreviousScreen, 2: NextScreen/Start */ { case 0: options_ready = 1; returnval = 0; break; case 2: currscreen = 1; break; /* default: currscreen+=0 */ } break; case 1: switch (tracksplit_parammenu (make_use_rms, make_graphs, blocklen, global_silence_factor, local_silence_threshold, min_silence_blocks, min_track_blocks, extra_blocks_start, extra_blocks_end)) /* 0: Cancel, 1: PreviousScreen, 2: NextScreen/Start */ { case 0: options_ready = 1; returnval = 0; break; case 1: currscreen = 0; break; case 2: returnval = 1; options_ready = 1; break; /* default: currscreen+=0 */ } break; } while (!options_ready); return returnval; } void tracksplit_merge (short *typearray, long *startarray, long *endarray, long *num_of_parts) { long l; long l2 = 0; for (l = 1; l < *num_of_parts; l++) if (typearray[l] == typearray[l2]) /* same type as previous -> merge */ endarray[l2] = endarray[l]; else { /* different */ l2++; typearray[l2] = typearray[l]; startarray[l2] = startarray[l]; endarray[l2] = endarray[l]; } *num_of_parts = l2 + 1; } void tracksplit_findtracks (double *medarray, /* inputs */ long total_blocks, double global_silence_threshold, int local_silence_threshold, int min_silence_blocks, int min_track_blocks, int extra_blocks_start, int extra_blocks_end, long *trackstarts, /* outputs */ long *trackends, int *detected_tracks) { double *above_threshold; double *above_th_rms; short *part_type; long *part_start; long *part_end; long num_parts; short type_now; short type_left, type_right; double local_mean; register long l; register long l2; double tempdouble; /* These modes must be >0 and all different */ #define POSSIBLE_TRACK 1 #define POSSIBLE_SILENCE 2 #define CERTAIN_TRACK 3 #define CERTAIN_SILENCE 4 #define SECOND_THRESHOLD 500 /* reserve space for arrays */ above_threshold = (double *) malloc (total_blocks * sizeof (double)); above_th_rms = (double *) malloc (total_blocks * sizeof (double)); part_type = (short *) malloc (total_blocks * sizeof (short)); part_start = (long *) malloc (total_blocks * sizeof (long)); part_end = (long *) malloc (total_blocks * sizeof (long)); /* apply global threshold */ for (l = 0; l < total_blocks; l++) { if (medarray[l] > global_silence_threshold) above_threshold[l] = 1000; else above_threshold[l] = 0; above_th_rms[l] = 0; } /* smooth the data a little by `running RMS' of length 11 */ for (l = 0; l < total_blocks; l++) { tempdouble = 0; for (l2 = l - 5; l2 <= l + 5; l2++) if (l2 >= 0 && l2 < total_blocks) tempdouble += above_threshold[l2] * above_threshold[l2]; /* else tempdouble += 0 */ above_th_rms[l] = sqrt (tempdouble / (2 * 5 + 1)); } /* before first track is certainly nothing (this is necessary for the computation later on) */ part_type[0] = CERTAIN_SILENCE; part_start[0] = -2; part_end[0] = -2; type_now = POSSIBLE_SILENCE; num_parts = 1; l2 = -1; /* l2 = start of current mode */ for (l = 0; l < total_blocks; l++) { if (type_now == POSSIBLE_SILENCE && above_th_rms[l] > SECOND_THRESHOLD) { part_type[num_parts] = type_now; part_start[num_parts] = l2; part_end[num_parts] = l - 1; l2 = l; type_now = POSSIBLE_TRACK; num_parts++; } if (type_now == POSSIBLE_TRACK && above_th_rms[l] <= SECOND_THRESHOLD) { part_type[num_parts] = type_now; part_start[num_parts] = l2; part_end[num_parts] = l - 1; l2 = l; type_now = POSSIBLE_SILENCE; num_parts++; } } /* close last track/silence */ part_type[num_parts] = type_now; part_start[num_parts] = l2; part_end[num_parts] = l - 1; l2 = l; num_parts++; /* after last block there's certainly nothing */ part_type[num_parts] = CERTAIN_SILENCE; part_start[num_parts] = l2; part_end[num_parts] = l2; num_parts++; /* search for certain tracks/silences */ for (l = 0; l < num_parts; l++) { if (part_type[l] == POSSIBLE_SILENCE && part_end[l] - part_start[l] + 1 >= min_silence_blocks) part_type[l] = CERTAIN_SILENCE; if (part_type[l] == POSSIBLE_TRACK && part_end[l] - part_start[l] + 1 >= min_track_blocks) part_type[l] = CERTAIN_TRACK; } #define notPART_DEBUG #ifdef PART_DEBUG for (l = 0; l < num_parts; l++) fprintf (stderr, "\n\rBlk: %ld Typ: %hd Start: %ld End: %ld", l, part_type[l], part_start[l], part_end[l]); sleep (5); #endif /* If first silence (blocks -1 .. ??) shorter than min_sil, it would be added in front of the first track. That's not what we want, so force it to be certain silence. Likewise with the last silence (if there is one). */ /* Part 0, block -2 only, already was certain silence; part 1 always starts at block -1 and is either possible or certain silence. */ if (part_type[1] == POSSIBLE_SILENCE) part_type[1] = CERTAIN_SILENCE; /* At this point, both _blocks_ -2 and -1 are certain silence. */ /* Part num_parts-1 is block total_blocks only, and is certain silence; part num_parts-2 may be all modes. */ if (part_type[num_parts - 2] == POSSIBLE_SILENCE) part_type[num_parts - 2] = CERTAIN_SILENCE; /* At this point, _block_ total_blocks is certain silence. */ /* Concluding, now all `virtual' blocks (outside range 0...total_blocks-1) are certain silence, and won't be (illegal) parts of tracks. */ /* Search for poss_sil poss_tr poss_sil, to account for ticks within silences; convert to (poss_sil)^3 This works, but may do some strange things. For example: c_t p_s p_t p_s c_t might become c_t p_s p_s p_s c_t -> c_t p_s c_t -> c_t c_s c_t. In words: a more-or-less common occurrence within one (1) track could get interpreted as a inter-track silence. Actually, this is not really a problem, because it is much easier to join two parts of one track than to manually split one `track' in two pieces at the right spot. So, it doesn't matter if there are a few silences too much. */ for (l = 1; l < num_parts - 1; l++) { if (part_type[l] == POSSIBLE_TRACK && part_type[l - 1] == POSSIBLE_SILENCE && part_type[l + 1] == POSSIBLE_SILENCE && part_end[l] - part_start[l] < 10) part_type[l] = POSSIBLE_SILENCE; } tracksplit_merge (part_type, part_start, part_end, &num_parts); /* now some (poss_sil)^3 sequences may be > min_silence_blocks, so check again */ for (l = 0; l < num_parts; l++) { if (part_type[l] == POSSIBLE_SILENCE && part_end[l] - part_start[l] + 1 >= min_silence_blocks) part_type[l] = CERTAIN_SILENCE; } /* More rules here? */ #ifdef PART_DEBUG for (l = 0; l < num_parts; l++) fprintf (stderr, "\n\rBlk: %ld Typ: %hd Start: %ld End: %ld", l, part_type[l], part_start[l], part_end[l]); sleep (5); #endif /* now resolve all other possible_* */ for (l = 0; l < num_parts; l++) if (part_type[l] == POSSIBLE_SILENCE || part_type[l] == POSSIBLE_TRACK) { /* search what is certain on left side */ l2 = l; type_left = 0; while (l2 >= 0 && type_left == 0) { if (part_type[l2] == CERTAIN_SILENCE || part_type[l2] == CERTAIN_TRACK) type_left = part_type[l2]; l2--; } if (type_left == 0) type_left = CERTAIN_SILENCE; /* search what is certain on right side */ l2 = l; type_right = 0; while (l2 < num_parts && type_right == 0) { if (part_type[l2] == CERTAIN_SILENCE || part_type[l2] == CERTAIN_TRACK) type_right = part_type[l2]; l2++; } if (type_right == 0) type_right = CERTAIN_SILENCE; /* decide what this one is going to be */ if (type_left == CERTAIN_SILENCE && type_right == CERTAIN_SILENCE) part_type[l] = CERTAIN_SILENCE; else /* CERTAIN_TRACK on either or both sides */ part_type[l] = CERTAIN_TRACK; } tracksplit_merge (part_type, part_start, part_end, &num_parts); /* Get rid of (illegal) blocks -2, -1 and total_blocks */ if (part_end[0] >= 0) part_start[0] = 0; else { for (l = 0; l < num_parts - 1; l++) { part_type[l] = part_type[l + 1]; part_start[l] = part_start[l + 1]; part_end[l] = part_end[l + 1]; } num_parts--; } if (part_start[num_parts - 1] <= total_blocks - 1) part_end[num_parts - 1] = total_blocks - 1; else num_parts--; #ifdef PART_DEBUG for (l = 0; l < num_parts; l++) fprintf (stderr, "\n\rBlk: %ld Typ: %hd Start: %ld End: %ld", l, part_type[l], part_start[l], part_end[l]); sleep (5); #endif /* fine-search for track starts/ends */ for (l = 0; l < num_parts; l++) if (part_type[l] == CERTAIN_SILENCE && part_end[l] - part_start[l] + 1 >= 10) { /* find local mean; exclude silence start/end */ local_mean = 0; for (l2 = part_start[l] + 3; l2 <= part_end[l] - 3; l2++) local_mean += medarray[l2]; local_mean /= (part_end[l] - part_start[l] + 1 - 6); while (medarray[part_start[l]] > local_mean * (1 + (local_silence_threshold / 100.)) && part_start[l] < part_end[l]) part_start[l]++; while (medarray[part_end[l]] > local_mean * (1 + (local_silence_threshold / 100.)) && part_end[l] > part_start[l]) part_end[l]--; /* minimize lost fade-in/out by adding extra blocks to track start/end (if possible) */ if (part_end[l] - part_start[l] + 1 >= extra_blocks_end + extra_blocks_start + 1) { part_start[l] += extra_blocks_end; part_end[l] -= extra_blocks_start; } /* Now silence may be too short, regard it as silence-in-a-track; except first and last silence. */ if (part_end[l] - part_start[l] + 1 < min_silence_blocks && l > 0 && l < num_parts - 1) part_type[l] = CERTAIN_TRACK; /* Adjust adjecent tracks */ if (l > 0) part_end[l - 1] = part_start[l] - 1; if (l < num_parts - 1) part_start[l + 1] = part_end[l] + 1; } /* if too-short-c_s -> c_t, we should merge c_t's */ tracksplit_merge (part_type, part_start, part_end, &num_parts); #ifdef PART_DEBUG for (l = 0; l < num_parts; l++) fprintf (stderr, "\n\rBlk: %ld Typ: %hd Start: %ld End: %ld", l, part_type[l], part_start[l], part_end[l]); sleep (5); #endif /* Make an array with tracks only */ *detected_tracks = 0; for (l = 0; l < num_parts && *detected_tracks < 99; l++) if (part_type[l] == CERTAIN_TRACK) { trackstarts[*detected_tracks] = part_start[l]; trackends[*detected_tracks] = part_end[l]; (*detected_tracks)++; } #if 0 /* TESTING: determine mean(localmeans) */ /* NB: what if NO certain silences? -> divide by 0 ?? */ tempdouble = 0; for (l = 0; l < num_parts; l++) if (part_type[l] == CERTAIN_SILENCE) { local_mean = 0; for (l2 = part_start[l]; l2 <= part_end[l]; l2++) local_mean += medarray[l2]; local_mean /= (part_end[l] - part_start[l] + 1); tempdouble += local_mean; } l2 = 0; for (l = 0; l < num_parts; l++) if (part_type[l] == CERTAIN_SILENCE) l2++; tempdouble /= l2; fprintf (stderr, "mean(locmean)= %f ", tempdouble); /* END TESTING */ #endif /* 0 */ free (above_threshold); free (above_th_rms); free (part_type); free (part_start); free (part_end); } void tracksplit_main (char *startdir) { char filename[250]; /* options menu defaults */ int make_use_rms = 1; int make_graphs = 0; long blocklen = 4410; int global_silence_factor = 150; int local_silence_threshold = 5; int min_silence_blocks = 20; int min_track_blocks = 50; int extra_blocks_start = 3; int extra_blocks_end = 6; sample_t sample; double *rmsarray = NULL; /* Otherwise gcc complains */ double *medarray; double *sortarray; long total_samples, total_samples_read; struct stat buf; register long l; long samples_read; long current_block, total_blocks; double sum_left, sum_right; int i; FILE *tempfile; FILE *tempfile2; #define dontCOMPUTE_GLOBAL_MEAN #ifdef COMPUTE_GLOBAL_MEAN double global_mean; #endif long trackstarts[100]; long trackends[100]; int detected_tracks; char tempstring[250]; double global_silence_threshold; double min_poss_threshold, max_poss_threshold; int compute_rms_now; long templong; int in_ch; filename[0] = '\0'; if (!tracksplit_get_options (startdir, filename, &make_use_rms, &make_graphs, &blocklen, &global_silence_factor, &local_silence_threshold, &min_silence_blocks, &min_track_blocks, &extra_blocks_start, &extra_blocks_end)) return; clearscreen (TRACKSPLIT_COMPUTE_HEADERTEXT); move (0, 79); refresh (); compute_rms_now = 0; /* Should we compute RMS? */ if (!make_use_rms) compute_rms_now = 1; if (!compute_rms_now) /* RMS must exist */ { strcpy (tempstring, filename); strcat (tempstring, ".rms"); tempfile = fopen (tempstring, "r"); if (tempfile == NULL) compute_rms_now = 1; } if (!compute_rms_now) /* Right RMS format */ { fgets (tempstring, 100, tempfile); if (strncasecmp (tempstring, "GramoFile Binary RMS Data", 25)) { fclose (tempfile); compute_rms_now = 1; } } if (!compute_rms_now) /* Read blocklen */ { if (fread (&templong, sizeof (long), 1, tempfile) < 1) { fclose (tempfile); compute_rms_now = 1; } } if (!compute_rms_now) /* blocklen same? */ { if (templong != blocklen) { fclose (tempfile); compute_rms_now = 1; } } if (!compute_rms_now) /* Read total_blocks */ { if (fread (&templong, sizeof (long), 1, tempfile) < 1) { fclose (tempfile); compute_rms_now = 1; } } if (!compute_rms_now) /* total_blocks reasonable? */ { if (templong < 1) { fclose (tempfile); compute_rms_now = 1; } else /* NOW we're satisfied */ { total_blocks = templong; rmsarray = (double *) malloc (total_blocks * sizeof (double)); fread (rmsarray, sizeof (double), total_blocks, tempfile); fclose (tempfile); } } if (compute_rms_now) /* Well, we can't be lazy always... */ { if (stat (filename, &buf)) { error_window ("Sound file could not be opened."); return; } total_samples = (buf.st_size - sizeof (wavhead)) / (2 * 2); total_blocks = (total_samples / blocklen) + 1; if (!openwavsource (filename)) { error_window ("Sound file could not be opened."); return; } error_window_display ("Computing signal power (RMS)...", " Cancel "); nodelay (stdscr, TRUE); rmsarray = (double *) malloc (total_blocks * sizeof (double)); total_samples_read = 0; current_block = 0; while (total_samples_read < total_samples) { if (!(current_block % 5)) { mvprintw (ERROR_WINDOW_Y + 2, ERROR_WINDOW_X + 1, "Done : %3d %%", (int) (current_block * 100. / total_blocks)); move (0, 79); } samples_read = 0; /* Compute RMS */ sum_left = 0; sum_right = 0; for (l = 0; l < blocklen; l++) if (total_samples_read < total_samples) { sample = readsamplesource (); sum_left += sample.left * (long) sample.left; sum_right += sample.right * (long) sample.right; /* (long) => faster & accurate */ samples_read++; total_samples_read++; } sum_left = sqrt (fabs (sum_left / samples_read)); sum_right = sqrt (fabs (sum_right / samples_read)); if (sum_right > sum_left) sum_left = sum_right; /* now left = max */ /* sum_left: 0..32767 but doublecheck */ if (sum_left < 0) sum_left = 0; if (sum_left > 32767) sum_left = 32767; rmsarray[current_block] = sum_left; /* store in array */ current_block++; in_ch = getch (); /* Check for keypress */ if (in_ch == 27 || in_ch == 13 || in_ch == KEY_ENTER) { reset_prog_mode (); nodelay (stdscr, FALSE); closewavsource (); return; } } closewavsource (); reset_prog_mode (); nodelay (stdscr, FALSE); if (make_use_rms) /* Write .RMS if requested */ { strcpy (tempstring, filename); strcat (tempstring, ".rms"); tempfile = fopen (tempstring, "w"); fprintf (tempfile, "GramoFile Binary RMS Data\n"); fwrite (&blocklen, sizeof (long), 1, tempfile); fwrite (&total_blocks, sizeof (long), 1, tempfile); fwrite (rmsarray, sizeof (double), total_blocks, tempfile); fclose (tempfile); } } /* if compute_rms */ /* RMS data available now. Start real work... */ medarray = (double *) malloc (total_blocks * sizeof (double)); sortarray = (double *) malloc (total_blocks * sizeof (double)); medarray[0] = rmsarray[0]; /* Take Median-3 */ medarray[total_blocks - 1] = rmsarray[total_blocks - 1]; /* A more or less optimized version of a median computation... */ for (l = 1; l < total_blocks - 1; l++) if (rmsarray[l - 1] < rmsarray[l]) /* a < b */ { if (rmsarray[l] < rmsarray[l + 1]) /* b < c */ medarray[l] = rmsarray[l]; else /* b > c */ { if (rmsarray[l - 1] < rmsarray[l + 1]) /* a < c */ medarray[l] = rmsarray[l + 1]; else /* c > a */ medarray[l] = rmsarray[l - 1]; } } else /* a > b */ { if (rmsarray[l] < rmsarray[l + 1]) /* b < c */ { if (rmsarray[l - 1] < rmsarray[l + 1]) /* a < c */ medarray[l] = rmsarray[l - 1]; else /* a > c */ medarray[l] = rmsarray[l + 1]; } else /* b > c */ medarray[l] = rmsarray[l]; } /* #define: up, up, up! */ #ifdef COMPUTE_GLOBAL_MEAN global_mean = 0; /* Global Mean - Not used any more */ for (l = 0; l < total_blocks; l++) global_mean += medarray[l]; global_mean /= total_blocks; /* fprintf(stderr, "\nGlobMean:%f\n",global_mean); sleep(5); */ #endif for (l = 0; l < total_blocks; l++) /* Fill arrays */ sortarray[l] = medarray[l]; qsort2double (sortarray, total_blocks); /* Sort meds. Fast! */ min_poss_threshold = sortarray[10]; max_poss_threshold = sortarray[total_blocks / 2]; #define dontDISPLAY_SOME_THRESHOLDS #ifdef DISPLAY_SOME_THRESHOLDS fprintf (stderr, "\n\r%f to %f\n\r", min_poss_threshold, max_poss_threshold); #define NUMTRIES 50 for (l = 0; l < NUMTRIES; l++) { /* Try some different thresholds between min_poss en max_poss; you need a looong xterm to see all of it ;-) */ global_silence_threshold = ((max_poss_threshold - min_poss_threshold) / (NUMTRIES - 1)) * l + min_poss_threshold; tracksplit_findtracks (medarray, total_blocks, global_silence_threshold, local_silence_threshold, min_silence_blocks, min_track_blocks, extra_blocks_start, extra_blocks_end, trackstarts, trackends, &detected_tracks); fprintf (stderr, "Glob.sil.thr.: %f Tracks: %d\n\r", global_silence_threshold, detected_tracks); } #endif /* DISPLAY_SOME_THRESHOLDS */ global_silence_threshold = (max_poss_threshold - min_poss_threshold) * (global_silence_factor / 1000.) + min_poss_threshold; /* fprintf(stderr, "New global threshold: %f\n\r", global_silence_threshold); */ tracksplit_findtracks (medarray, total_blocks, global_silence_threshold, local_silence_threshold, min_silence_blocks, min_track_blocks, extra_blocks_start, extra_blocks_end, trackstarts, trackends, &detected_tracks); /* sleep(5); */ /* Write the .tracks file */ strcpy (tempstring, filename); strcat (tempstring, ".tracks"); tempfile = fopen (tempstring, "w"); if (tempfile == NULL) { error_window ("The .tracks file could not be written."); return; } fprintf (tempfile, "\ # GramoFile Tracks File\n\ #\n\ # This file contains information on track starts/ends. It is automatically\n\ # generated and will be overwritten completely by subsequent track-\n\ # splitting actions on the same audio file.\n\ \n\ # Blank lines and lines starting with `#' are ignored.\n\ \n\ [Tracks]\n\ # These values are not used (yet), but are included for reference /\n\ # regeneration purposes.\n\ "); fprintf (tempfile, "Blocklen=%ld\n", blocklen); fprintf (tempfile, "Global_silence_factor=%d\n", global_silence_factor); fprintf (tempfile, "Local_silence_factor=%d\n", local_silence_threshold); fprintf (tempfile, "Min_silence_blocks=%d\n", min_silence_blocks); fprintf (tempfile, "Min_track_blocks=%d\n", min_track_blocks); fprintf (tempfile, "Extra_blocks_start=%d\n", extra_blocks_start); fprintf (tempfile, "Extra_blocks_end=%d\n", extra_blocks_end); fprintf (tempfile, "\ \n\ # Below are start/end times of tracks. These are used to create separate\n\ # soundfiles during signal processing. You may modify the computed values\n\ # if you disagree... The block-numbers are those used in the .med file.\n\ \n\ "); fprintf (tempfile, "Number_of_tracks=%d\n", detected_tracks); for (l = 0; l < detected_tracks; l++) { fsec2hmsf ((trackends[l] - trackstarts[l] + 1) * (double) blocklen / 44100., tempstring); fprintf (tempfile, "\n# Track %ld - blocks %ld to %ld - length: %s\n", l + 1, trackstarts[l], trackends[l], tempstring); fsec2hmsf (trackstarts[l] * (double) blocklen / 44100., tempstring); fprintf (tempfile, "Track%02ldstart=%s\n", l + 1, tempstring); fsec2hmsf ((trackends[l] + 1) * (double) blocklen / 44100., tempstring); fprintf (tempfile, "Track%02ldend=%s\n", l + 1, tempstring); } fprintf (tempfile, "\n"); fclose (tempfile); if (make_graphs) /* Write graphs */ { strcpy (tempstring, filename); strcat (tempstring, ".med"); tempfile2 = fopen (tempstring, "w"); fprintf (tempfile2, "Med(RMS(signal))\n"); fprintf (tempfile2, "Threshold: %f\n\n", global_silence_threshold); for (l = 0; l < total_blocks; l++) { fprintf (tempfile2, "%5ld:%8.2f ", l, medarray[l]); for (i = 0; i < fabs (medarray[l] / 80); i++) fprintf (tempfile2, "="); fprintf (tempfile2, "\n"); } fclose (tempfile2); strcpy (tempstring, filename); strcat (tempstring, ".sor"); tempfile2 = fopen (tempstring, "w"); fprintf (tempfile2, "Sort(Med(RMS(signal)))\n"); fprintf (tempfile2, "Threshold: %f\n\n", global_silence_threshold); for (l = 0; l < total_blocks; l++) { fprintf (tempfile2, "%5ld:%8.2f ", l, sortarray[l]); for (i = 0; i < fabs (sortarray[l] / 80); i++) fprintf (tempfile2, "="); fprintf (tempfile2, "\n"); } fclose (tempfile2); } sprintf (tempstring, "%d tracks have been detected. More information \ is in the `.tracks' file.", detected_tracks); error_window (tempstring); free (rmsarray); free (medarray); free (sortarray); } gramofile-1.6/tracksplit.h100644 1751 144 622 7070217416 14556 0ustar costarusers/* Determining Track Start/End - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_TRACKSPLIT_H #define HAVE_TRACKSPLIT_H #define TRACKSPLIT_COMPUTE_HEADERTEXT "Track Location" void tracksplit_main (char *startdir); #endif /* HAVE_TARACKSPLIT_H */ gramofile-1.6/gmffileio.c100644 1751 144 10055 7070217415 14372 0ustar costarusers THESE ROUTINES ARE NOT USED YET (MAYBE NEVER...) /* .GMF file I/O * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ /*#include "gmffileio.h" */ #include #include /* structure = better? typedef struct { FILE *file; char filename[250]; ...? } gmffile_t; */ FILE *create_gmf (char *filename) /* Makes new .gmf file, writes comment-headers */ { } FILE * open_gmf (char *filename) /* Opens a .gmf file for in/output. Returns NULL if not successful */ { FILE *gmffile; gmffile = fopen (filename, "r"); return gmffile; } int close_gmf (FILE * gmffile) { return fclose (gmffile); } int read_line_from_gmf (FILE * gmffile, char **line) /* Reads 1 line from gmf file, terminated by 10 or 13 or EOF. 1: successful, 0: not (EOF). line will be malloc'ed. If successful, file pointer will be on start of next line */ { long oldpos; long numofchars = 0; int i; oldpos = ftell (gmffile); do { i = fgetc (gmffile); numofchars++; } while (i != 10 && i != 13 && i != EOF); /* numofchars is including 10, 13 or EOF, so always >= 1 */ if (numofchars == 1 && i == EOF) return 0; /* End of file */ *line = (char *) malloc (numofchars * sizeof (char)); if (fseek (gmffile, oldpos, SEEK_SET)) return 0; for (i = 0; i < numofchars; i++) (*line)[i] = fgetc (gmffile); (*line)[numofchars - 1] = '\0'; return 1; } char * strip_spaces (char *instring) /* Strips spaces from beginning and end. Return-string will be malloc'ed */ { char *outstring; char *startptr; char *endptr; char *tempcharptr; startptr = instring; while ((*startptr == ' ' || *startptr == 9) && *startptr != '\0') startptr++; /* startptr now on real start of text */ endptr = startptr; while (*endptr != '\0') endptr++; /* endptr now on last character */ while ((*endptr == ' ' || *endptr == 9 || *endptr == '\0') && endptr > startptr) endptr--; /* endptr now on real end of text */ outstring = (char *) malloc (((endptr + 2) - startptr) * sizeof (char)); outstring[(endptr + 1) - startptr] = '\0'; tempcharptr = outstring; while (startptr <= endptr) { *tempcharptr = *startptr; tempcharptr++; startptr++; } return outstring; } void del_comments (char *line) /* strip_comments -> '\0' if starting with '#'. So `line' is modified. */ { if (line[0] = '#') line[0] = '\0'; } int search_identifier (FILE * gmffile, char *identifier) /* Returns 1 if found, 0 if not found */ { rewind (gmffile); } int read_simpval_from_gmf (FILE * gmffile, char *identifier, char *value) /* Reads simple value; `value' will be malloc'ed. Returns 1: OK, 0: failure (not present, etc.) */ { } void write_simpval_to_gmf (FILE * gmffile, char *identifier, char *value) /* Writes simple value `identifier = value', discarding old value if present */ { } int start_read_list_from_gmf (FILE * gmffile, char *identifier) /* Start reading a list. Returns 1: OK, 0: failure (not present, etc.) */ { } int read_listitem_from_gmf (FILE * gmffile, char *value) /* Read one value from a list that has been started using start_read_list_from_gmf(). `value' will be malloc'ed. Returns 1: OK, 0: failure (no more items, etc.) */ { } void start_write_list_to_gmf (FILE * gmffile, char *identifier) /* Start writing a (new) list, discarding old contents if present */ { } void write_listitem_to_gmf (FILE * gmffile, char *value) /* Writes one item to list opened by start_write_list_to_gmf() */ { } void stop_write_list_to_gmf (FILE * gmffile) /* Closes writing of the list opened by start_write_list_to_gmf() */ { } main () { char *line = 0; char *line2 = 0; FILE *gmffile; gmffile = open_gmf ("/tmp/test.gmf"); while (read_line_from_gmf (gmffile, &line)) { printf ("%p>%s<\n", line, line); line2 = strip_spaces (line); printf ("%p>%s<\n", line2, line2); free (line); free (line2); } close_gmf (gmffile); } gramofile-1.6/gmf.specs100644 1751 144 2636 6552372223 14066 0ustar costarusers THIS FORMAT IS NOT USED YET (MAYBE NEVER...) # Sample .gmf file - GramoFile info/description/data file # Copyright (C) 1998 J.A. Bezemer # # Licensed under the terms of the GNU General Public License. # ABSOLUTELY NO WARRANTY. # See the file `COPYING' in this directory. # # Lines starting with (whitespace followed by) `#' are comments. # `#' in mid-line is part of that line (NO comment). # Blank lines are allowed and ignored. # # Format: # Simple option: # = # Identifier may contain any character including whitespace (depreciated). # Whitespace between start of line and Identifier and between Identifier # and `='-character is ignored. # Value may contain any character. Whitespace between `='-character and # Value and between Value and end-of-line is ignored. One pair of `'' # or `"' at beginning and end of Value will be stripped if present # at both ends and both the same character. Note: Value may contain # `#' which is NOT treated as comment. # # Composite option: # = { , , ... , } # Identifier and Value are treated as described above. The Value-list # may contain line breaks just before, just after or instead of # the `,' . Value-lines may be interleaved with comment-lines, but # `{' must be on the same line as the Identifier. LongName = Folksongs from Rumania Artist = Fieraru & Mihalcea Recorded = 23 Jun 98 Side = 1 Filters = { } gramofile-1.6/tracksplit_filenm.h100644 1751 144 765 7070217416 16120 0ustar costarusers/* Tracksplitting - Get file name - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_TRACKSPLIT_FILENM_H #define HAVE_TRACKSPLIT_FILENM_H #define TRACKSPLIT_FILE_HEADERTEXT "Track Location - File Name" int tracksplit_select_file (char *startdir, char *selectedfile); /* Returns 0: canceled, 2: NextScreen */ #endif /* HAVE_TRACKSPLIT_FILENM_H */ gramofile-1.6/tracksplit_parammenu.c100644 1751 144 46207 7070217416 16667 0ustar costarusers/* Tracksplitting - Parameters * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "tracksplit_parammenu.h" #include "errorwindow.h" #include "stringinput.h" #include "buttons.h" #include "clrscr.h" #include "boxes.h" #include "helpline.h" #include #include #ifndef OLD_CURSES #include #else #include #endif int tracksplit_parammenu (int *make_use_rms, int *make_graphs, long *blocklen, int *global_silence_factor, int *local_silence_threshold, int *min_silence_blocks, int *min_track_blocks, int *extra_blocks_start, int *extra_blocks_end) /* Returns 0: canceled, 1: PrevScreen, 2: NextScreen */ { button_t next_button, cancel_button, prev_button; button_t make_use_rms_check, make_graphs_check; stringinput_t blocklen_string, global_silence_factor_string, local_silence_threshold_string, min_silence_blocks_string, min_track_blocks_string, extra_blocks_start_string, extra_blocks_end_string; int dont_stop = TRUE; int returnval = 0; int focus = 11; int in_ch; long templong; char *helplines[12] = { " Save results or use saved results of lengthy computation. TAB: Next field", " For manual (double-)checking/adjustment. Needs a few MB's. TAB: Next field", " Number of samples to compute RMS of; 4410 = 0.1 sec. TAB: Next field", " The relative threshold during the initial search for tracks. TAB: Next field", " Begin/end of track ...% above local (silence) power level. TAB: Next field", " Shorter periods of silence not treated as track separation. TAB: Next field", " Shorter tracks are ignored. TAB: Next field", " Correction for fade-in effects that are hard to detect. TAB: Next field", " Correction for fade-out effects that are hard to detect. TAB: Next field", " To Track Splitting - File Name. TAB: Next field", " Back to main menu. TAB: Next field", " Start searching for tracks. TAB: Next field"}; make_use_rms_check.text = ""; /* see below */ make_use_rms_check.y = 4; make_use_rms_check.x = 2; make_use_rms_check.selected = FALSE; make_graphs_check.text = ""; /* see below */ make_graphs_check.y = 6; make_graphs_check.x = 2; make_graphs_check.selected = FALSE; blocklen_string.maxlen = 500; blocklen_string.string = (char *) malloc ( blocklen_string.maxlen * sizeof (char)); sprintf (blocklen_string.string, "%ld", *blocklen); blocklen_string.y = 8; blocklen_string.x = 54; blocklen_string.w = 12; blocklen_string.cursorpos = strlen (blocklen_string.string); blocklen_string.firstcharonscreen = 0; global_silence_factor_string.maxlen = 500; global_silence_factor_string.string = (char *) malloc ( global_silence_factor_string.maxlen * sizeof (char)); sprintf (global_silence_factor_string.string, "%d", *global_silence_factor); global_silence_factor_string.y = 10; global_silence_factor_string.x = 54; global_silence_factor_string.w = 12; global_silence_factor_string.cursorpos = strlen (global_silence_factor_string.string); global_silence_factor_string.firstcharonscreen = 0; local_silence_threshold_string.maxlen = 500; local_silence_threshold_string.string = (char *) malloc ( local_silence_threshold_string.maxlen * sizeof (char)); sprintf (local_silence_threshold_string.string, "%d", *local_silence_threshold); local_silence_threshold_string.y = 11; local_silence_threshold_string.x = 54; local_silence_threshold_string.w = 12; local_silence_threshold_string.cursorpos = strlen (local_silence_threshold_string.string); local_silence_threshold_string.firstcharonscreen = 0; min_silence_blocks_string.maxlen = 500; min_silence_blocks_string.string = (char *) malloc ( min_silence_blocks_string.maxlen * sizeof (char)); sprintf (min_silence_blocks_string.string, "%d", *min_silence_blocks); min_silence_blocks_string.y = 13; min_silence_blocks_string.x = 54; min_silence_blocks_string.w = 12; min_silence_blocks_string.cursorpos = strlen (min_silence_blocks_string.string); min_silence_blocks_string.firstcharonscreen = 0; min_track_blocks_string.maxlen = 500; min_track_blocks_string.string = (char *) malloc ( min_track_blocks_string.maxlen * sizeof (char)); sprintf (min_track_blocks_string.string, "%d", *min_track_blocks); min_track_blocks_string.y = 14; min_track_blocks_string.x = 54; min_track_blocks_string.w = 12; min_track_blocks_string.cursorpos = strlen (min_track_blocks_string.string); min_track_blocks_string.firstcharonscreen = 0; extra_blocks_start_string.maxlen = 500; extra_blocks_start_string.string = (char *) malloc ( extra_blocks_start_string.maxlen * sizeof (char)); sprintf (extra_blocks_start_string.string, "%d", *extra_blocks_start); extra_blocks_start_string.y = 16; extra_blocks_start_string.x = 54; extra_blocks_start_string.w = 12; extra_blocks_start_string.cursorpos = strlen (extra_blocks_start_string.string); extra_blocks_start_string.firstcharonscreen = 0; extra_blocks_end_string.maxlen = 500; extra_blocks_end_string.string = (char *) malloc ( extra_blocks_end_string.maxlen * sizeof (char)); sprintf (extra_blocks_end_string.string, "%d", *extra_blocks_end); extra_blocks_end_string.y = 17; extra_blocks_end_string.x = 54; extra_blocks_end_string.w = 12; extra_blocks_end_string.cursorpos = strlen (extra_blocks_end_string.string); extra_blocks_end_string.firstcharonscreen = 0; prev_button.text = " < Previous screen "; prev_button.y = 20; prev_button.x = 5; prev_button.selected = FALSE; next_button.text = " Start computation "; next_button.y = 20; next_button.x = 56; next_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 36; cancel_button.selected = FALSE; clearscreen (TRACKSPLIT_PARAMMENU_HEADERTEXT); do { header (TRACKSPLIT_PARAMMENU_HEADERTEXT); if (*make_use_rms) make_use_rms_check.text = "[X]"; else make_use_rms_check.text = "[ ]"; if (*make_graphs) make_graphs_check.text = "[X]"; else make_graphs_check.text = "[ ]"; if (focus == 0) make_use_rms_check.selected = TRUE; else make_use_rms_check.selected = FALSE; if (focus == 1) make_graphs_check.selected = TRUE; else make_graphs_check.selected = FALSE; if (focus == 9) prev_button.selected = TRUE; else prev_button.selected = FALSE; if (focus == 10) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 11) next_button.selected = TRUE; else next_button.selected = FALSE; stringinput_display (&blocklen_string); mvprintw (blocklen_string.y, 2, "Length of blocks of signal power data (samples) :"); stringinput_display (&global_silence_factor_string); mvprintw (global_silence_factor_string.y, 2, "Global silence factor (0.1 %) :"); stringinput_display (&local_silence_threshold_string); mvprintw (local_silence_threshold_string.y, 2, "Local silence factor (%) :"); stringinput_display (&min_silence_blocks_string); mvprintw (min_silence_blocks_string.y, 2, "Minimal length of inter-track silence (blocks) :"); stringinput_display (&min_track_blocks_string); mvprintw (min_track_blocks_string.y, 2, "Minimal length of tracks (blocks) :"); stringinput_display (&extra_blocks_start_string); mvprintw (extra_blocks_start_string.y, 2, "Number of extra blocks at track start :"); stringinput_display (&extra_blocks_end_string); mvprintw (extra_blocks_end_string.y, 2, "Number of extra blocks at track end :"); button_display (&prev_button); mybox (prev_button.y - 1, prev_button.x - 1, 3, strlen (prev_button.text) + 2); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&next_button); mybox (next_button.y - 1, next_button.x - 1, 3, strlen (next_button.text) + 2); button_display (&make_use_rms_check); mvprintw (make_use_rms_check.y, make_use_rms_check.x + 4, "Save/load signal power (RMS) data to/from .rms file"); button_display (&make_graphs_check); mvprintw (make_graphs_check.y, make_graphs_check.x + 4, "Generate graph files"); helpline (helplines[focus]); switch (focus) { case 2: stringinput_display (&blocklen_string); break; case 3: stringinput_display (&global_silence_factor_string); break; case 4: stringinput_display (&local_silence_threshold_string); break; case 5: stringinput_display (&min_silence_blocks_string); break; case 6: stringinput_display (&min_track_blocks_string); break; case 7: stringinput_display (&extra_blocks_start_string); break; case 8: stringinput_display (&extra_blocks_end_string); break; default: move (0, 79); } refresh (); in_ch = getch (); switch (focus) { case 0: /* Make/use .RMS file */ switch (in_ch) { case KEY_ENTER: case 13: case ' ': case 'x': case 'X': *make_use_rms = 1 - *make_use_rms; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 1: /* Make graph files */ switch (in_ch) { case KEY_ENTER: case 13: case ' ': case 'x': case 'X': *make_graphs = 1 - *make_graphs; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 2: /* Block length */ stringinput_stdkeys (in_ch, &blocklen_string); switch (in_ch) { case KEY_ENTER: case 13: templong = atol (blocklen_string.string); if (templong < 1) { error_window ("Enter a whole number, greater than 0. \ Default: 4410."); blocklen_string.cursorpos = strlen (blocklen_string.string); } else { *blocklen = templong; sprintf (blocklen_string.string, "%ld", templong); blocklen_string.cursorpos = strlen (blocklen_string.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 3: /* Global silence factor */ stringinput_stdkeys (in_ch, &global_silence_factor_string); switch (in_ch) { case KEY_ENTER: case 13: templong = atol ( global_silence_factor_string.string); if (templong < 0 || templong > 10000) { error_window ("Enter a whole, positive number, preferably \ less than 1000. Default: 150."); global_silence_factor_string.cursorpos = strlen ( global_silence_factor_string.string); } else { *global_silence_factor = templong; sprintf (global_silence_factor_string.string, "%ld", templong); global_silence_factor_string.cursorpos = strlen (global_silence_factor_string.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 4: /* Local silence threshold */ stringinput_stdkeys (in_ch, &local_silence_threshold_string); switch (in_ch) { case KEY_ENTER: case 13: templong = atol ( local_silence_threshold_string.string); if (templong < 0 || templong > 10000) { error_window ("Enter a whole, positive number. \ Default: 5."); local_silence_threshold_string.cursorpos = strlen ( local_silence_threshold_string.string); } else { *local_silence_threshold = templong; sprintf (local_silence_threshold_string.string, "%ld", templong); local_silence_threshold_string.cursorpos = strlen ( local_silence_threshold_string.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 5: /* Min. silence length (blocks) */ stringinput_stdkeys (in_ch, &min_silence_blocks_string); switch (in_ch) { case KEY_ENTER: case 13: templong = atol ( min_silence_blocks_string.string); if (templong < 0 || templong > 10000) { error_window ("Enter a whole, positive number. \ Default: 20."); min_silence_blocks_string.cursorpos = strlen ( min_silence_blocks_string.string); } else { *min_silence_blocks = templong; sprintf (min_silence_blocks_string.string, "%ld", templong); min_silence_blocks_string.cursorpos = strlen ( min_silence_blocks_string.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 6: /* Min. track length (blocks) */ stringinput_stdkeys (in_ch, &min_track_blocks_string); switch (in_ch) { case KEY_ENTER: case 13: templong = atol ( min_track_blocks_string.string); if (templong < 0 || templong > 10000) { error_window ("Enter a whole, positive number. \ Default: 50."); min_track_blocks_string.cursorpos = strlen ( min_track_blocks_string.string); } else { *min_track_blocks = templong; sprintf (min_track_blocks_string.string, "%ld", templong); min_track_blocks_string.cursorpos = strlen ( min_track_blocks_string.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 7: /* Extra blocks at track start */ stringinput_stdkeys (in_ch, &extra_blocks_start_string); switch (in_ch) { case KEY_ENTER: case 13: templong = atol ( extra_blocks_start_string.string); if (templong < 0 || templong > 10000) { error_window ("Enter a whole, positive number. \ Default: 3."); extra_blocks_start_string.cursorpos = strlen ( extra_blocks_start_string.string); } else { *extra_blocks_start = templong; sprintf (extra_blocks_start_string.string, "%ld", templong); extra_blocks_start_string.cursorpos = strlen ( extra_blocks_start_string.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 8: /* Extra blocks at track end */ stringinput_stdkeys (in_ch, &extra_blocks_end_string); switch (in_ch) { case KEY_ENTER: case 13: templong = atol ( extra_blocks_end_string.string); if (templong < 0 || templong > 10000) { error_window ("Enter a whole, positive number. \ Default: 6."); extra_blocks_end_string.cursorpos = strlen ( extra_blocks_end_string.string); } else { *extra_blocks_end = templong; sprintf (extra_blocks_end_string.string, "%ld", templong); extra_blocks_end_string.cursorpos = strlen ( extra_blocks_end_string.string); focus++; } break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 9: /* < Previous */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 1; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 10: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 11: /* Next > */ if (in_ch == KEY_ENTER || in_ch == 13) { templong = atol (blocklen_string.string); if (templong < 1) { error_window ("Enter a whole number, greater than 0, as \ block length. Default: 4410."); blocklen_string.cursorpos = strlen (blocklen_string.string); focus = 2; break; } *blocklen = templong; templong = atol (global_silence_factor_string.string); if (templong < 1 || templong > 10000) { error_window ("Enter a whole, positive number as \ global silence factor. Default: 150."); global_silence_factor_string.cursorpos = strlen (global_silence_factor_string.string); focus = 3; break; } *global_silence_factor = templong; templong = atol (local_silence_threshold_string.string); if (templong < 1 || templong > 10000) { error_window ("Enter a whole, positive number as \ local silence factor. Default: 5."); local_silence_threshold_string.cursorpos = strlen (local_silence_threshold_string.string); focus = 4; break; } *local_silence_threshold = templong; templong = atol (min_silence_blocks_string.string); if (templong < 1 || templong > 10000) { error_window ("Enter a whole, positive number as \ minimal silence duration. Default: 20."); min_silence_blocks_string.cursorpos = strlen (min_silence_blocks_string.string); focus = 5; break; } *min_silence_blocks = templong; templong = atol (min_track_blocks_string.string); if (templong < 1 || templong > 10000) { error_window ("Enter a whole, positive number as \ minimal track length. Default: 50."); min_track_blocks_string.cursorpos = strlen (min_track_blocks_string.string); focus = 6; break; } *min_track_blocks = templong; templong = atol (extra_blocks_start_string.string); if (templong < 1 || templong > 10000) { error_window ("Enter a whole, positive number as \ block addition. Default: 3."); extra_blocks_start_string.cursorpos = strlen (extra_blocks_start_string.string); focus = 7; break; } *extra_blocks_start = templong; templong = atol (extra_blocks_end_string.string); if (templong < 1 || templong > 10000) { error_window ("Enter a whole, positive number as \ block addition. Default: 6."); extra_blocks_end_string.cursorpos = strlen (extra_blocks_end_string.string); focus = 8; break; } *extra_blocks_end = templong; returnval = 2; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } /* switch(focus) */ if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 11) focus = 0; if (focus < 0) focus = 11; } while (dont_stop); return returnval; } gramofile-1.6/tracksplit_filenm.c100644 1751 144 15724 7070217416 16154 0ustar costarusers/* Track Splitting - Get file-name * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "tracksplit_filenm.h" #include "scrollmenu.h" #include "stringinput.h" #include "buttons.h" #include "boxes.h" #include "dirfilemenu.h" #include "errorwindow.h" #include "textwindow.h" #include "checkfile.h" #include "yesnowindow.h" #include "helpline.h" #include "clrscr.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif int tracksplit_select_file (char *startdir, char *selectedfile) /* Returns 0: canceled, 2: NextScreen */ { scrollmenu_t dirfilelist; stringinput_t string; button_t next_button, cancel_button; int dont_stop = TRUE; int returnval = 0; int focus = 1; int in_ch; int i; char helpstring[500]; char *charpointer; struct stat filestats; int oldselected; char *helplines[4] = { " Select name of sound file for track splitting. TAB: Next field", " Enter name of sound file for track splitting. TAB: Next field", " Back to main menu. TAB: Next field", " To Track Splitting - Parameters. TAB: Next field"}; dirfilelist.y = 3; dirfilelist.x = 5; dirfilelist.h = 12; dirfilelist.w = 32; dirfilelist.firstonscreen = 0; dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = dirfilelist.last_of_1st_part + 1; string.maxlen = 500; string.string = (char *) malloc (string.maxlen * sizeof (char)); if (selectedfile[0] == '\0') strcpy (string.string, startdir); else strcpy (string.string, selectedfile); string.y = 17; string.x = 5; string.w = 70; string.cursorpos = strlen (string.string); string.firstcharonscreen = strlen (string.string) - string.w + 2; if (string.firstcharonscreen < 0) string.firstcharonscreen = 0; next_button.text = " Next screen > "; next_button.y = 20; next_button.x = 60; next_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 36; cancel_button.selected = FALSE; clearscreen (TRACKSPLIT_FILE_HEADERTEXT); do { header (TRACKSPLIT_FILE_HEADERTEXT); if (focus == 2) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 3) next_button.selected = TRUE; else next_button.selected = FALSE; dirfilelist.hasfocus = (focus == 0); scrollmenu_display (&dirfilelist); mybox (dirfilelist.y - 1, dirfilelist.x - 1, dirfilelist.h + 2, dirfilelist.w + 2); mvprintw (dirfilelist.y - 1, dirfilelist.x + 1, "Files and directories:"); stringinput_display (&string); mybox (string.y - 1, string.x - 1, 3, string.w + 2); mvprintw (string.y - 1, string.x + 1, "File name:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&next_button); mybox (next_button.y - 1, next_button.x - 1, 3, strlen (next_button.text) + 2); helpline (helplines[focus]); if (focus == 1) stringinput_display (&string); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* dirfilelist */ if (scrollmenu_stdkeys (in_ch, &dirfilelist) >= 0) { oldselected = dirfilelist.selected; i = dirfilemenu_process_select (&dirfilelist, helpstring); if (i == 0) /* filename in helpstring */ { strcpy (string.string, helpstring); focus = 1; string.cursorpos = strlen (string.string); string.firstcharonscreen = 0; } else /* dir in helpstring */ { scrollmenu_delete_menu (&dirfilelist); dirfilemenu (helpstring, &dirfilelist); if (dirfilelist.number == 0) { error_window ( "No permission to read this directory."); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.selected = oldselected; } else { strcpy (startdir, helpstring); dirfilelist.firstonscreen = 0; charpointer = strrchr (string.string, '/'); if (charpointer != NULL) strcat (helpstring, charpointer + 1); else strcat (helpstring, string.string); strcpy (string.string, helpstring); } } } else switch (in_ch) { case KEY_LEFT: focus--; break; case KEY_RIGHT: focus++; break; } break; case 1: /* string */ stringinput_stdkeys (in_ch, &string); if (in_ch == KEY_ENTER || in_ch == 13) { strcpy (helpstring, string.string); /* cut away last '/'-s */ while (strlen (helpstring) > 0 && helpstring[strlen (helpstring) - 1] == '/') helpstring[strlen (helpstring) - 1] = '\0'; strcat (helpstring, "/"); if (!stat (helpstring, &filestats) && S_ISDIR (filestats.st_mode)) { strcpy (startdir, helpstring); scrollmenu_delete_menu (&dirfilelist); dirfilemenu (startdir, &dirfilelist); dirfilelist.firstonscreen = 0; dirfilelist.selected = dirfilelist.last_of_1st_part + 1; strcpy (string.string, startdir); string.cursorpos = strlen (string.string); focus = 0; } else /* it's a file */ focus = 3; } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 3: /* Next > */ if (in_ch == KEY_ENTER || in_ch == 13) switch (checkfile (string.string)) { case FILE_EXISTS: strcpy (selectedfile, string.string); returnval = 2; dont_stop = FALSE; break; case DIR_EXISTS: error_window ("The specified name is of a directory. A \ file name must be specified."); string.cursorpos = strlen (string.string); focus = 1; break; case DIR_OK_NEW_FILE: case DIR_WRONG: error_window ("The specified file does not exist."); string.cursorpos = strlen (string.string); focus = 1; break; default: error_window ("Fell out of switch, tracksplit_filenm #1"); break; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 3) focus = 0; if (focus < 0) focus = 3; } while (dont_stop); scrollmenu_delete_menu (&dirfilelist); free (string.string); return returnval; } gramofile-1.6/tracksplit_parammenu.h100644 1751 144 1322 7070217416 16641 0ustar costarusers/* Tracksplitting - Parameters - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_TRACKSPLIT_PARAMMENU_H #define HAVE_TRACKSPLIT_PARAMMENU_H #define TRACKSPLIT_PARAMMENU_HEADERTEXT "Track Location - Parameters" int tracksplit_parammenu (int *make_use_rms, int *make_graphs, long *blocklen, int *global_silence_factor, int *local_silence_threshold, int *min_silence_blocks, int *min_track_blocks, int *extra_blocks_start, int *extra_blocks_end); /* Returns 0: canceled, 1: PrevScreen, 2: NextScreen */ #endif /* HAVE_TRACKSPLIT_PARAMMENU_H */ gramofile-1.6/example.tracks100644 1751 144 2420 6546434527 15122 0ustar costarusers# GramoFile Tracks File # # This file contains information on track starts/ends. It is automatically # generated and will be overwritten completely by subsequent track- # splitting actions on the same audio file. # Blank lines and lines starting with `#' are ignored. [Tracks] Blocklen=4410 Local_silence_threshold=8 Min_silence_blocks=30 Min_track_blocks=50 Extra_blocks_start=3 Extra_blocks_end=3 # Below are start/end times of tracks. These are used to create separate # soundfiles during signal processing. You may modify the computed values # if you disagree... The block-numbers are those used in the .med file. Number_of_tracks=8 # Track 1 - blocks 55 to 1745. Track01start=0:00:05.500 Track01end=0:02:54.600 # Track 2 - blocks 1798 to 2618. Track02start=0:02:59.800 Track02end=0:04:21.900 # Track 3 - blocks 2663 to 3385. Track03start=0:04:26.300 Track03end=0:05:38.600 # Track 4 - blocks 3434 to 4697. Track04start=0:05:43.400 Track04end=0:07:49.800 # Track 5 - blocks 4761 to 6198. Track05start=0:07:56.100 Track05end=0:10:19.900 # Track 6 - blocks 6246 to 7702. Track06start=0:10:24.600 Track06end=0:12:50.300 # Track 7 - blocks 7749 to 8926. Track07start=0:12:54.900 Track07end=0:14:52.700 # Track 8 - blocks 8980 to 9717. Track08start=0:14:58.000 Track08end=0:16:11.800 gramofile-1.6/Tracksplit.txt100644 1751 144 34704 6612120306 15146 0ustar costarusers || NOTE: This document describes the track location algorithms found in || versions 1.0 - 1.3 of GramoFile. The new algorithm found in version 1.4 || and up is described in the file Tracksplit2.txt. Track Splitting in GramoFile A successful approach to a problem of old times By J. A. Bezemer J.A.Bezemer@ITS.TUDelft.NL Last update: 07/07/1998 GramoFile website: http://cardit.et.tudelft.nl/~card06 INTRODUCTION Only a few years ago, gramophone records were the most popular way of distributing music. These days, their place has been taken entirely by Compact Discs. Some of the most popular records have been digitally remastered, and CDs with music from the `good old times' have been pressed. After the introduction of CD-Recordables and CD Recorders for use with Personal Computers, some people started making CDs from their own records. It is desirable to have tracks on the CD just as they were on the original gramophone record. This can be done by sampling each individual track of the record to a seperate audio file on the harddisk of the PC, and then use a piece of CD writing software that can make separate tracks of a row of audio files. This way, the sampling process is laborious and requires constant attention. In this document, a process is described that automates the separation of tracks. An entire side of a record is sampled to one big audio file, and an intelligent piece of software automatically detects where tracks begin and end. This process has been implemented in the GramoFile program, that is also capable of applying anti-tick signal processing to audio files. The routines of that program will be mentioned a few times in this document. REDUCTION OF DATA The size of a CD-quality audio file (.wav format) that represents sampled audio of a gramophone record, is about 200 megabytes. This is far more data than is needed to compute track starts and ends. Therefore, a large scale reduction of the data is possible. It is also necessary, because processing several hunderd megabytes of data will take far too long to be useful, even on the fastest computers available. One way to characterize tracks with respect to inter-track silence, is the fact that the signal power (or volume) is higher. The signal power may be computed with the root-mean-square (RMS) expression __________________________ / i=0 _ / 1 --- 2 y[t] = RMS(x[t]) = | / --- ) ( x[t+i] ) |/ N --- / N-1 with N the `length' of the RMS operation. When calculating the RMS of an audio signal for tracksplitting purposes, a good choise for the length is one tenth second. Then there are enough samples to compute an accurate RMS (4410 when using CD-quality sampling), there are sufficient (typ. >30) RMS-points within an inter-track silence, and the total number of RMS points is limited enough to fit completely in a computer's memory (typ. <100 kB). If stereo signals are used, there may be some soft signal at one channel, while there is only silence on the other channel. Therefore, it is advisable to compute the RMS of both channels separately, and then store the maximum of the two values in a RMS list (array). Figure 1 dipalys an example sequence of RMS data. | | | || || || ||| || | || | | || ||||||| |||||||||||||| ||| |||||| ||||| |||||||||||| ||||||||||||||||| |||||||||| |||||||||||||||||| ||||||||||||||||| ||||||||||| ||||||||||||||||||| |||||||||||||||||| |||||||||||| ||||||||||||||||||||| |||||||||||||||||||| ||||||||||||| ||||||||||||||||||||||| ||||||||||||||||||||||| |||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ^ ^ ^ ^ silence silence silence silence Figure 1. Example sequence of RMS data. Tracks are recognized immediately as they have a much larger RMS value than silence. BEAUTIFYING THE RMS DATA The RMS data is rather `raw', and contains a number of `ticks'. To beautify the data a little, a median filter can be used. This filter, that is described in detail in the file Signproc.txt, has the property that it filters out short disturbances. After median filtering with a length of 3, the RMS data looks much more `neat'. DETERMINING THE SILENCE THRESHOLD When the median-filtered RMS data is studied more closely, it becomes clear that during the inter-track silence, there is no silence at all. To the contrary, the average signal power (that's the RMS) is relatively high. That is because a gramophone record has little disturbances everywhere, like ticks or the relatively rough surface the needle rests upon. However, there still is a clearly defined separation bewteen the various tracks, at least to humans that look at the median filtered RMS data. Inter-track silence is characterized as such by a prolonged period of a very low RMS value. The questions that need to be answered are therfore how long the interval must be, and what the `very low RMS value' is. The former may be answered immediately, as practically all records have 5, but certainly more that 3, seconds of silence between tracks. The latter is more complex, for it will be different for all records. Thus, a method needs to be developed for the automatic deduction of the correct `silence threshold' of the RMS value. A very interesting observation can be made if the RMS data is sorted by value. This is depicted in figure 2. | || |||| |||||| |||||||||| |||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ^ ^ collected silence music Figure 2. Example of RMS data that is sorted by value. It is clear, that all silence values are moved to the left of graph, and all music values to the right. When we walk through the graph from left to right, first we encounter a lot of silence. When the silence is over, the music starts. And an interesting phenomenon occurs just there, namely that there is a very quick transition from the silence level to the soft-music level. Theoretically, this was to be expected, since even soft music must be well above a certain power level, to maintain a reasonable signal-to-noise ratio. The values that occur between silence level and soft-music level arise from fade-in and fade-out effects. The middle of this sharp level-transition is easily detectable by computing the differential for each position, as in diff(x[t]) = x[t+5] - x[t-5] The `differential-length' of 11 is taken that large to enhance the desired effect. In figure 3, an example of the differential is displayed. | || | ||| | ||||| ||| ||||||||| ||||| |||||||||||| |||||| || || |||| ||||| ||||||||||||||||| |||||||||||||| |||||||||||||||| ||||||||||||||||||||||||||||||||| || |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ^ maximum Figure 3. Example of differentials of the sorted RMS data. Where the differential reaches its maximum (to be more precise, the maximum of the first part), the slope of the sorted RMS data is maximal. As pointed out above, this point doesn't really belong to either silence or music. And, as could be expected, it turns out that this value is perfectly useable as silence threshold. Note that this threshold is decuded completely from the signal itself, and its inherent (theoretically plausible) properties. No user-interaction is either necessary or possible. BETTER ESTIMATION OF STARTS AND ENDS The first detection of inter-track silence takes place by walking through the median filtered RMS data, searching for locations where the RMS value is below the silence threshold for a period of minimally, say, 30 tenth-seconds. This way, the silences begin and end on the places where the silence threshold is crossed. Usually, these positions are not quite correct, as the fade-ins and fade-outs are not handled correctly. Thus problem may be addressed by finding another (lower) threshold, that excludes fade-ins and fade-outs from the inter-track silence. Generally, these new thresholds are different for each individual inter-track silence on the record. So, another way of deducing the new threshold must be developed. A quite straightforward way of doing this, is to compute the mean of all RMS values in the silence, and setting the threshold a few percent above this mean. To find a correct mean, a few (e.g. 5) points at the beginning and end of the `coarse' silence are excluded, to prevent the fade-ins and fade-outs from pulling the mean too high. In praxis, a threshold of 8% above the (local) mean (1,08 times the local mean) is generally found to be acceptable. The median-filtered RMS data is now scanned from the previously detected start of the silence forward and from the end backward to the first and last points the RMS drops below the new threshold. These new points are incremented, respectively decremented, again by a fixed number of points (e.g. 3) to account for all possible circumstances. Note that this method requires one user-specifiable parameter, namely the percentage of the threshold above the local mean. However, the mentioned value of 8% should be correct for most cases. THE LIST OF TRACKS If the silences are detected, there is no problem in building a list of track starts and ends. For music is there where no silence is. But there may be `fake' tracks at the beginning and end of the big audio file, so an extra selection may be appied, for example discarding all tracks that are shorter than 5 seconds. IMPLEMENTATION IN GRAMOFILE The method for splitting tracks as described above, has been implemented in the GramoFile program. It is accessible with the `Determine track separation points' in the main menu. After the selection of the audio file that is to be splitted, a screen appears with several adjustable parameters. The first parameter is the length (in samples) of the volume blocks. With CD quality sampling, there are 44100 samples per second, so one tenth second is 4410 samples. Then there is the local silence threshold (in percent), minimal length of inter-track silence and tracks themselves, and the number of blocks that should be added to tracks after the fine-detection. All these parameters are described above. Because the computation of the RMS values (signal power / volume information) takes rather long, there is an option to save/load the RMS data in/from a .rms file (.rms appended to the audio file name, like side_1.wav.rms). If the .rms file does not exist, or if it is made with another block-length as requested, it is (re-)generated. This may take quite a while, dependant on the speed of the computer used. If the .rms file is present and useable, it will be used right away, so the time consuming recalculation is avoided. In that case, the audio (.wav) file is not accessed. Finally, there is an option `Generate graph files'. When this option is turned on, a few files will be created, namely: - .med : graph of the median filtered RMS data, like figure 1 - .sor : graph of the sorted median filtered RMS data, like figure 2 - .dif : graph of the differential of the sorted data, like figure 3 - .drms : RMS filtered version of the .dif file GramoFile actually uses a RMS filtered version of the differential to detect the maximum slope in the sorted data. When track detection is done, a .tracks file will be written. This file will be used by the Signal Processing to create separate audio files for each track mentioned in the .tracks file. The .tracks file is a .ini-style plaintext file, and may be edited. Currently, only the Track??start end Track??end values are used (and the Number_of_tracks of course); the others are mainly for your own reference. The various graph files provide much information that can be used to verify or correct the times mentioned in the .tracks file. RESULTS AND CONCLUSION With the GramoFile program, several gramophone records have been recorded, and have had their tracks split. The results were very satisfying. That is mainly because practically all required parameters are deduced from the audio signal itself. Some more tests will be performed, in order to guarantee the correctness of the algorithm in most circumstances. On the GramoFile website, http://cardit.et.tudelft.nl/~card06, a few examples of the various graph files are published. If you encounter a situation in which the track splitting doesn't function properly, you can contact me about it. Please include gzip'd versions of both the .rms and .tracks files you have problems with, and describe your problem as clearly and completely as possible. Also, corrections, additions, suggestions and compliments are heartily welcomed. Contact information is in the general `README' file. gramofile-1.6/Signproc.txt100644 1751 144 77516 6552362340 14633 0ustar costarusers Signal Processing in GramoFile A discussion of implemented filters, framework and technical details By J. A. Bezemer J.A.Bezemer@ITS.TUDelft.NL Last update: 07/13/1998 GramoFile website: http://cardit.et.tudelft.nl/~card06 INTRODUCTION The processing of digital signals is a major part of the GramoFile program. This processing is done by so-called filters, as in the `Conditional Median Filter'. To allow easy programming and application of these filters, a flexible framework has been implemented. This allows filters to be used in a random order (even multiple times) in one single run. Each filter has its own set of (user-changeable) parameters. This may be visualized as shown below. ___________ ________ ________ ___________ | | | | | |proc'd | | | Harddisk |signal | | | |signal | Harddisk | | |------>|Filter 1|- - - ->|Filter N|------>| | | .wav file | | | | | | .wav file | |___________| |________| |________| |___________| Figure 1. The signal processing structure. The GramoFile user interface is used to specify what filters should be used, and in what order. The parameters of each filter may also be changed using the user interface. This is the option `Process the audio signal' in the Main Menu. In this version of GramoFile, four filters have been implemented: - Simple Mean Filter - Simple Median Filter - Double Median Filter - Conditional Median Filter In the remainder of this document these four filters will be described in more detail. Also more information is given on the filter framework, and how to implement new filters. SIMPLE MEAN FILTER The Simple Mean Filter is the most simple filter in GramoFile. It takes the (unweighted) mean of an adjustable number of samples, see figure 2 below. input signal x[t-3] x[t-2] x[t-1] x[t] x[t+1] x[t+2] x[t+3] \______________ ______________/ \ / Mean | V output signal y[t-3] y[t-2] y[t-1] y[t] y[t+1] y[t+2] y[t+3] Figure 2. The Simple Mean Filter. In a mathematical expression: i=-N 1 --- y[t] = ---- ) x[t+i] 2N+1 --- N where the `length of the filter' is 2N+1; in figure 2 the length is 5, so there N=2. The effect of this filter on an audio signal with ticks, is that the ticks will be `spread out', and be somewhat reduced in strength. However, they still remain clearly audible. But also the `clear' audio signal is affected. With filter lengths greater than 5, the quality of the output audio signal degrades rapidly. This filter behaves like a crude lowpass filter. SIMPLE MEDIAN FILTER The Simple Median Filter computes the median of an adjustable number of samples. A well-known property of a `running median' is its ability to filter out short impulses (like ticks) in a signal. The median of a row of numbers is the middle one if the row is sorted by value. To find the median of the numbers 5, 3, 8, 1, 2, we sort them first, resulting in 1, 2, 3, 5, 8, and then take the middle one, 3 (note that the mean is different, namely 19/5 = 3.8). The impulse-filtering property is illustrated in figure 3, where a (distorted) signal is filtered with a running medians of length 3 and 5, so every time the median of 3 or 5 samples is taken. input signal: median-3 filtered: median-5 filtered: | | | | | |||| | ||||| ||||| | ||||| | |||||| |||||| || | || ||||| || | || ||||||||||||| | ||||||||||||| ||| |||||||||| ||||| |||||||||||||||||||| |||||||||||||||||||| 22105122134545042121 22111222234454422211 11211222234444422211 Figure 3. Examples of running medians with lengths 3 and 5. In figure 3, it is clear that all disturbances have disappeared in the filtered signals. To be more precise, all disturbances with a length of 1 and 2, respectively, are filtered out. Generally, a median of 2N+1 samples will filter out all disturbances with a length smaller than or equal to N. In audio signals, ticks tend to have a length of more than, say, 5 samples (CD-quality sampling). So when median filtering with length 11 is applied, most ticks disappear. But also the sound itself is badly affected. Most of the high frequencies are lost, and an annoying noise is added to the signal. This filter may be used to determine the best size for the median filtering of the Conditional Median Filter. DOUBLE MEDIAN FILTER The Double Median Filter is described by R.R. Lawrence (et al.) in `Applications of a Nonlinear Smoothing Algorithm to Speech Processing', IEEE Transactions on Acoustics, Speech, and Signal Processing, Vol. ASSP-23, No. 6, Dec 1975. It is composed of a `normal' median filter and a median filter that filters the error (disturbance) signal. It is depicted in figure 4. ________ input | | + output -------+--->| Median |-----+--------------------->O---------> | |________| | ^ + | | | | | ________ | | V - | | | +------------------>O------->| Median |----+ + error |________| Figure 4. Block diagram of the Double Median Filter. The idea is, that the addition of the median of the error signal should enhance the quality of the output signal. But during tests, there was not too much audible difference with the Simple Median Filter. CONDITIONAL MEDIAN FILTER The Conditional Median Filter is described in detail by T. Kasparis (et al.) in `Adaptive Scratch Noise Filtering', IEEE Transactions on Consumer Electronics, Vol. 39, No. 4, Nov 1993. The basic functionality is 1) detect ticks, 2) interpolate if there is a tick. Ticks are mostly `composed of' high frequencies. Undisturbed audio signal (especially from gramophone records) only has relatively low frequencies. This observation is used to detect ticks and scratches. The interpolation is performed by a median filter, see above (Simple Median Filter) for its interpolating properties. The block diagram of the Conditional Median Filter is shown in figure 5. __________ input x[t] | | output y[t] ----------+---------------------------------->| Median |-------------> | | Filter | _____V_____ |__________| | | ^ | Highpass | | | Filter | | g[t] |___________| | | _____|_____ | w[t] | | +---------------------------------->| | | _____ ___________ | Gate | | | | | | | | | | | | | Recursive | b[t] | Generator | +-->| V K |---->| Median |------>| | |_____| | Filter | |___________| |___________| Figure 5. Block diagram of the Conditional Median Filter. The highpass filter in reality is a combination of a second-order derivative, and a Root-Mean-Square filter. The second order derivative is z[t] = x[t-1] - 2 x[t] + x[t+1] and the RMS is -------------------------- / i=-N _ / 1 --- 2 w[t] = | / ---- ) ( z[t+i] ) |/ 2N+1 --- / N where 2N+1 is the `length' of the RMS operation. The resulting signal w[t] has large values if there is a tick, and is very `quiet' otherwise. But w[t] is not 0 if there are no ticks, it has a certain `background noise' level. That background level is estimated by taking the median of a large number of samples of w[t]. The best results are accomplished using a recursive median filter, defined as y[t] = median ( y[t-M], ... , y[t-1], x[t], x[t+1], ... , x[t+M] ) where x[t] is the input and y[t] the output. To reduce calculation time, the signal w[t] is decimated first (only one of K samples is considered). The estimated background level resulting from decimation and recursive median filtering is b[t]. The gate generator is (currently) nothing more than a simple condition, namely / w[t] - b[t] | 1, if ----------- > C | b[t] g[t] = < | | 0, otherwise \ where C is a certain factor, for example 2.5. If the gate signal g[t] is 1, the `main' median filter is `switched on', letting it compute medians, otherwise the signal x[t] passes unchanged. In the Properties screen of the Conditional Median Filter, several parameters van be changed: - Length of the `main' median filter - Length of the RMS operation on z[t] in the Highpass Filter (2N+1) - Length of the Recursive Median Filter (2M+1) - Decimation factor (K) - Threshold for gate generation (C) The optimal length of the `main' median filter may be found by using the Simple Median Filter on a piece of badly disturbed audio signal. During tests, the Conditional Median Filter has given excellent results. With the default properties (21 - 9 - 11 - 5 - 2500), nearly all ticks are filtered out, without audible losses in signal quality. But some rare musical instruments generate tones that can fool the tick detection algorithm, resulting in faulty interpolation. If this occurs, try to use the properties 15 - 11 - 9 - 4 - 2500. THE FILTER FRAMEWORK (Well, we're getting more technical every minute...) Audio samples don't travel through filters out of free will. Actually, they are pulled through by the harddisk. This is made visible in figure 6. ___________ ________ ________ ___________ | | Q | | | | Q | | | Harddisk |<------| |<- - - -| |<------| Harddisk | | | |Filter 1| |Filter N| | | | .wav file |------>| |- - - ->| |------>| .wav file | |___________| S |________| |________| S |___________| Figure 6. Questions and answers (Samples) in the signal processing structure The destination file on the harddisk `asks' the last filter for the next sample (that's the Q of Question). But a filter can't produce an output sample without an input sample. So the last filter asks the previous filter for the next sample. Those questions continue till the first filter is reached. That filter `asks' the source file on the harddisk. Then the first filter can compute its output sample, and passes it through to the second filter. When all filters have computed their outputs, the destination file eventually gets the next (processed) sample. This process is easily implemented in C, as illustrated in listing 1 below. sample_t filter1() { sample_t nextsample; sample_t procd_sample; nextsample = read_from_file(); /* Do processing */ return procd_sample; } sample_t filter2() { sample_t nextsample; sample_t procd_sample; nextsample = filter1(); /* Do processing */ return procd_sample; } write_funtion() { sample_t nextsample; for (...) { nextsample = filter2(); write_to_file(nextsample); } } Listing 1. Example implementation of questions and answers. By calling filter2(), the write_function() effectively asks for the next sample, and stores the answer in the variable nextsample. The filters themselves ask the previous ones, and eventually the harddisk. With only one sample traveling, there are only very limited possibilities of filtering. Most filters need a few samples around the current one. For example, the Simple Median Filter with length 3 will need samples x[t-1], x[t], and x[t+1]. To accommodate this, so-called `buffers' have been implemented. These buffers can `remember' and `read ahead' a few samples. This is illustrated in figure 7. ___________ ________ ________ ___________ | | Q | : | | : | Q | | | Harddisk |<------| : Filt1|<- - - -| : FiltN|<------| Harddisk | | | |B: | |B: | | | | .wav file |------>| : |- - - ->| : |------>| .wav file | |___________| S |_:______| |_:______| S |___________| Figure 7. Buffers (B) in the signal processing structure. Questions (Q) and returned samples (S) stay the same. When the harddisk-writing-routine asks for the next sample, the `effective time' of the last filter is incremented by 1. So the buffer `window' of that filter advances one time unit. The oldest sample is discarded (e.g. the old x[t-2], now x[t-3]), and a new sample is needed (e.g. the new x[t+2]). So that buffer asks the previous filter to supply the sample for time-index t+2. Then t+2 is the effective time for the last-but-one filter. In general, all filters have different effective times. When starting the filtering process, all filters have an effective time t=0. But all buffers must get filled before the first sample appears at the output of the last filter, so first all buffers will fill up and the effective times will be adjusted automatically. During the rest of the process, the differences in effective times will remain constant. But, you don't have to understand anything about effective times. That's all embedded in the buffer routines. And it's working fine. When we zoom in a little, the structure becomes more clear, figure 8. ________ | | +-------------|advance_| | |current_|<-------------------+ | +------->|pos() | | params | | S |________| params | | Q | | |S | | _V___ _V____|_ _V_ ___V____ __|___ | Q |get_ | |___| \ | | Q | | |<----|sample_ | |___| | |get_ |<----| |<--- Filt1| |from_ | |___| |->|from_ | |Filt 2| |---->|filter()| |___| | |buffer()|---->| |---> _____| S |________| |___| / |________| S |______| buffer Figure 8. Interconnection of filters and buffers. A filter can read from its buffer using the get_from_buffer function. This function has a parameter `offset', to specify which sample should be returned. An offset of -2 means the x[t-2] sample. The function advance_current_pos has the effect of increasing the effective time by 1. In other words, it moves the buffer `window' one time unit further. The oldest sample is shifted out (discarded), and a brand new one must be shifted in. To accomplish this, the function get_sample_from_filter is called. This routine determines which filter should be questioned for the next sample, gets and returns it to the advance_current_pos function, that shifts it in the buffer. The advance_current_pos function is called once at the very beginning of each filter routine. The first time the advance_current_pos function is called, the buffer is empty. Advance_current_pos takes care that it is filled correctly, the first part with zeroes (silence) and from x[t] to x[t+N] with new samples, calling the get_sample_from_filter routine repeatedly. So, after each call to advance_current_pos, the buffer is completely filled and up-to-date. Each filter has its own set of parameters, that are changeable via the GramoFile user interface. Both filter 1 and filter 2 may be `instances' of the same filter `type'. For example, both may be Simple Median Filters. The software routines for the filters then are the same. The only things that are different are the parameters and the buffers. Actually, the buffer is seen as another parameter. During the initialization of the filtering process, a number of parameter-sets are created in memory. The get_sample_from_filter not only finds out which filter it should ask for the next sample, also it tells the filter which set of parameters to use. That is why the filters are declared like sample_t simple_median_filter(parampointer_t parampointer) accepting a (pointer to a) set of parameters, and returning the computed sample. IMPLEMENTING NEW FILTERS Implementing a new filter shouldn't be that difficult. You don't have to think about reading or writing from or to a .wav file, because that has been implemented already. As are the buffer-functions. The only thing you have to worry about is the filtering algorith itself. It is a good idea to look at a simple example, to get an idea of how it works. Let's take the Simple Mean Filter (in signpr_mean.c). There are five functions: - reset the parameters to some reasonable defaults - provide a properties-screen by which the parameters may be changed - initialize the buffers and other parameters, called just before the signal processing starts - delete the buffer data structures that have been used, called after the signal processing has ended - the filter itself A variable of the parampointer_t type is just a pointer to a param_t structure. Both are declared in signpr_general.h. As pointed out above, the buffers are part of the parameters (3 per filter, see below). Then there is a `filterno', that is the serial number of the filter, in other words the position of this particular filter in the `Selected Filters' panel in the `Signal Processing - Options' screen. Both buffers and filterno are filled in by the init_*_filter() functions. The parameters that are settable in the user interface (using the *_param_screen() functions) are mostly pre- and postlengths, and an int and long type variable (just create more if necessary). The `postlength' is the number of samples that a buffer must `remember', the `prelength' is the `read-ahead' amount. Int1 is for example used for the decimation factor, and long1 for the threshold level. There are also some sslists, signed-short-arrays, to avoid continuous creation/deletion of local arrays, in an attempt so speed up the signal processing process, but you don't have to use these. The Simple Mean Filter is a easily understandable example of a signal processing routine, and it doesn't need any more explanation. Note that advance_current_pos() is called first of all. The Simple Median Filter (signpr_median.c) uses the sslists to temporarily copy the entire buffer to be sorted, in order to determine the median. That takes place in the median() routine that calls the qsort2() routine, both in signpr_general.c. The very fast sorting routine was adapted from the example by L. Ammeraal in `Ansi C', Academic Service, Schoonhoven, The Netherlands, 2nd ed., 1993. The Double Median Filter uses two buffers. That is needed because two median functions are present, and the second uses the results of the first. The block diagram from figure 4 is printed again below, now including names for all signals. ________ x[t] | | z[t] + y[t] -------+--->| Median |-----+--------------------->O---------> | |________| | ^ + | | | | | ________ | | V - | | | +------------------>O------->| Median |----+ + e[t] |________| c[t] Figure 9. Block diagram of the Double Median Filter. Below are example sequences of the various signals. Note that every signal can be computed completely when all upper signals are known. This is the way you would do it by hand. t<0 : x[t] 0 : 100 50 30 80 90 10 70 50 40 20 10 80 20 : / \___ ___/ : / \/ z[t] 0 : 50 |50 50 80 80 70 50 50 40 20 20 20 =med(x) : \ | / : \| / e[t] 0 : 50 0 -20 0 10 -60 20 0 0 | 0 -10 60 =x-z : \___ ___/ | : \/ | c[t] 0 : 0 0 0 0 0 10 0 0 0 | 0 0 =med(e) : \ | : \| y[t] 0 : 50 50 50 80 80 80 50 50 40 20 20 =z+c : Figure 10. Example signals for the Double Median Filter, when both median-lengths are 3. The lines point at the values needed to find the lowest one. All filers for which you can find the answer in this way can be implemented in GramoFile. But you must be inventive with the use of buffers. Generally speaking, every computation that needs a sample other than one directly above it, will need a buffer. With the Double Median Filter, both z[t] and x[t] will need one. Well, we _can_ do with only one, but that's much much slower... I've implemented the Double Median Filter as shown below. ______________________________________ x[t] | |-4|-3|-2|-1|+0|+1|+2|+3|+4|+5|+6| | |__|__|__|__|__|__|__|__|__|__|__|__|__| \_____ ______/ | V | | .------------' Median | | z[t] | __________________V_ | |-3|-2|-1|+0|+1|+2|+3| z[t] | |__|__|__|__|__|__|__| | \ | - | \ | V `-----------. `----------->O | + | e[t] | V | \__________ _________/ | V | | | Median | | c[t] | + V | O<------------' | + z[t] V y[t] Figure 11. Schematic of a possible implementation of the Double Median Filter. The original idea was to buffer e[t], but then we've lost z[t], which we need to compute y[t]. This way there is only a little drawback, for we now have to do one addition extra when we need e[t]. The second buffer is implemented in a similar way as the `normal' buffer, it is accessible through the get_from_buffer() function. But filling the buffer works somewhat different, as the next sample can't be asked from the previous filter. The next sample must (generally) be computed from the contents of the `normal' buffer. To allow this, there is a function advance_current_pos_custom() that accepts a fill-function as parameter. This fill-function will be called to supply the value for the samples in the second buffer. As can be seen in figure 11, the fill-function for the second buffer of the Double Median Filter will return the median of a row of samples in the first buffer. Which samples should be considered, is dependent on the position of the to-be-computed sample in the second buffer. For example, z[t] is the median of x[t-2] through x[t+2], and z[t+5] is the median of x[t+3] through x[t+7]. The fill-function needs to know where to look, so some parameters have to be supplied to it, namely offset and offset_zero. Offset is the position in the second buffer, and offset_zero is the position of the `0'-position in the second buffer relative to the `0'-position in the first buffer. In the case of figure 11, offset is +3 and offset_zero is 0. Offset is supplied by advance_current_pos_custom(), and offset_zero must be supplied to advance_current_pos_custom(), that will pass it through. Advance_current_pos_custom() should be called immediately after the call to the ordinary advance_current_pos(), in order to assure up-to-date contents of all buffers. In the case of the Double Median Filter, the main routine (the one that is called by get_sample_from_filter()), is double_median_filter(). The first thing is the customary call of advance_current_pos() that updates the first buffer. Then the second buffer may be updated, so advance_current_pos_custom() is called. The first argument is the buffer that should be updated, buffer2 in this case. The next argument is the fill-function, or, to be more specific, a pointer to that function. That pointer is declared just above double_median_filter(), and is of the fillfuncpointer_t type, defined in signpr_general.h. The third argument is the offset_zero. In this case, the second buffer is `fixed' at offset_zero=0. The last argument is the omnipresent parampointer, with the invaluable information about pre/postlengths etc. The fill-function double_median_filter_1() is much like an ordinary filter, only advance_current_pos() isn't called here (already done in the main routine). But the computation and returning of the computed sample are still there. Note that, during the computation, the offset and offset_zero are used to determine the interval of the median. In this case, the offset and offset_zero are added, as they will be in most cases. Offset_zero will always be 0, but is left for completeness. Back in double_median_filter(), operation continues normally. The structure of figure 11 is clearly visible. The `j /= 2' is there to prevent overflow in case sample and sample2 have different signs and large values. Compensation after computation of the median. Yes, this is really wrong, but then this filter is not meant for real use... The Conditional Median Filter (signpr_cmf.c) is even more complex, as it uses three buffers. The structure is illustrated in figure 12. __________________________________________________ x[t] | |-4|-3|-2|-1|+0|+1|+2|+3|+4|+5|+6|+7|+8|+9| | | |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__| \__ ___/ \___________ ____________/ V V | | Highpass ,-------------------' | z[t] | __________________V_ | z[t] |-3|-2|-1|+0|+1|+2|+3| | |__|__|__|__|__|__|__| | \________ _________/ | V | | | ,--------------. RMS | | | | w[t] | | _________V_________________V_ | | |-3|-2|-1|+0|+1|+2|+3|+4|+5|+6| w[t] | | |__|__|__|__|__|__|__|__|__|__| | | \ | | `---------------------. | | | | | \________ ___ ___ ___ ___ __/ | | | V (decimate) | | | | | | | Median | | | b[t] | | | | /| ,--------------------' | `------------' | | w[t] | V V | Compare | | g[t] V gate | Median <----------------' | | V y[t] Figure 12. Schematic of the implementation of the Conditional Median Filter. The general structure of the implementation resembles that of the Double Median Filter. Some custom fill-functions are used to fill the two `extra' buffers. In particular the buffer of w[t] is necessary, due to the recursive nature of the computation that is done with it. The recursive median is implemented as an ordinary median, but after each computation, the result is stored again in the w[t] buffer. This is dune with the put_in_buffer() function (we grab w_t before putting the new value in its place). Decimation is accomoplished by selecting only a few of the pre-read samples of w[t], and computing the median thereof. In the main filter routine, cond_median_filter(), there is first the customary call of advance_current_pos, and thereafter only one call to advance_current_pos_custom(), for the third buffer that is fixed at offset_zero=0. The second buffer is not fixed, but `floats' with the to-be-computed offset in the third buffer. The second buffer is therefore updated in the fill-function of the third buffer. The offset_zero of the second buffer is offset + offset_zero (both of the third buffer, the latter being 0). In the situation of figure 12, the offset in the third buffer is +6, so the offset_zero of the second buffer also is +6. The fill-function of the second buffer is supplied with its own offset, +3 in the case of figure 12. The second derivative (`highpass operation') now needs to be computed for the offset+offset_zero = 9th sample in the first buffer. Special care should be taken when allocating the first buffer in the init_*_filter() functions, as there may be more places where the samples in the buffer are accessed. In the Conditional Median Filter, the samples in the first buffer are accessed by the main routine, that computes the median if needed, and by the fill-function of the second buffer, that computes the second derivative (highpass). So, the buffer must be big enough to accommodate the median. The highpass function is different, as it `floats'. During the first filling of the buffers (when the first sample is asked by the harddisk), all offsets and offset_zeroes are 0, so the highpass needs samples x[t-1], x[t] and x[t+1]. When all buffers are filled, the middle of the highpass lies at the added values of the `prelengths' of the second and third filter. In the case of figure 12, the minimal prelength of the first buffer is 6 + 3 + 1 (the `right side' of the second derivative) = 10. In that example, the median requires x[t-4] through x[t+4], and the highpass requires x[t-1] through x[t+10], so the first buffer must at least contain x[t-4] through x[t+10]. If you want to implement a new filter, it is generally a good idea to start with a numeric example like figure 10. It gives a good overview of the filter, and also a way to verify the correctness of the implementation in a later phase. With such a numeric example, the drawing of a diagram like figures 11 and 12 isn't too difficult. After that, implementing should be straightforward. If there are problems with either this document or the implementation of a new filter, you can contact me. See the general `README' file for more information. If it is an implementation problem, the inclusion of drawings like figures 4, 5, 9, 10, 11 and 12 is highly recommended. Also, if you have succeeded in implementing a new filter, please send the source files and documentation to me, so that it can be included in a next release of GramoFile. gramofile-1.6/gramofile.c100644 1751 144 1736 7070217415 14364 0ustar costarusers/* GramoFile - Main * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "mainmenu.h" #include "splashscr.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif void init_curses (void) { initscr (); keypad (stdscr, TRUE); nonl (); cbreak (); noecho (); /* Xterms erase everything after the first refresh, so refresh one time before anything is added. */ refresh (); return; } static void finishmenu (int sig) { endwin (); exit (0); } void main (void) { char startdir[250]; char *helpcharptr; signal (SIGINT, finishmenu); init_curses (); splashscreen (); helpcharptr = getcwd (startdir, 250); if (helpcharptr == NULL) strcpy (startdir, "/"); else strcat (startdir, "/"); mainmenu (startdir); finishmenu (0); } gramofile-1.6/ChangeLog100644 1751 144 10314 7070215723 14035 0ustar costarusersGramoFile Change Log version 1.6: - Included semun definition that disappeared from glibc2.1 headers. Bart Warmerdam was the first to submit this to the Debian Bug Tracking system. - Allow piping the signal processing's output to any command (MP3 encoders!), thanks to Geoff Clare. See README for details. - Settings for filtering are now remembered within one run, which makes it much easier to try slight variations in the settings. Patch by James Tappin. - New convert-to-mono (=average-channels) filter, also by James Tappin. - James Tappin also provided an alternative way of interpolation for the CMF-II, using an L1 norm linear fit. It's much slower; you can compile it in by defining FANCY_FILL in the beginning of signpr_cmf2.c. - Reverted curses to ncurses per default, should now compile fine on SuSE. For the old curses, change $LIBS in the Makefiles and use -DOLD_CURSES. - Keith Refson sent a scandir() replacement function for Solaris; see the README for info on how to use it. - For byte-swapped systems, the length-fields in the .wav header are now byte-swapped too; patch also by Keith Refson. - FreeBSD patches by Juergen Lock. - Applied fix for a buffering deadlock at the end of recording, also by Juergen Lock. - James Tappin ported Dave Platt's out-of-phase tick detection (see v1.3) to the `old' CMF filter; search for TEST_DAVE_PLATT in signpr_cmf.c. - Fixed bug in the parameter screen of the double median filter. version 1.5: - Signal Processing 30-50% faster, thanks to a few "old programmers' tricks" provided by Dave Platt. - Fix for computation of file offsets and -lengths for long files in bplay, and 12 bit/sample should now work correctly, too. Patch by Ralf Schlatterbeck. - Should compile & run on IRIX (and maybe others), too. Joshua Weage sent me a patch, which I interpreted a little more generally. More info in the README file. version 1.4: - `Determining of track separation points' is now called simply `Track location'. - Totally new track location algorithm that should work well in far more cases than you're used to ;-) Read more in Tracksplit2.txt. - Should compile OK with egcs; patch by Daniel Kobras. version 1.3: - Considerable speed improvement in Signal Processing (and Track Splitting, too) by using the stdio functions, patch provided by Axel Kohlmeyer. (Why didn't anyone tell me speed(fread)>>speed(read) before..?) - Deleting filters in Filter Selection screen now also works on the console (using the R key), again thanks to Axel Kohlmeyer. (I'm always in X ;) - Info-screen after recording, great idea of Richard Robinson. - Dave Platt reported that most ticks are out-of-phase, so tick detection should look at the left-minus-right (difference) signal. I've programmed this in signpr_cmf2.c (search for TEST_DAVE_PLATT) but not tested it; it's not used by default. If you like, you may experiment with it - and tell me your findings. version 1.2: - Added variable median (interpolation) length to the Conditional Median Filter, and called it CMF II, so the original is still there. The CMF II gives much better results than the original CMF. - Added the possibility of playing whole tracks, or a few seconds at the beginning/end of a track or before/after a track - with a single keystroke. Makes life a lot easier. - Added a Copy Only `filter' that only copies the signal. Useful for splitting tracks without processing the signal. Requested (more or less) by Richard Robinson. - Added a Experimenting Filter with which you can experiment. See the file signpr_exper.c - Corrected writing of .wav header - bytes_per_sample seems to be 4 instead of 2 (as found in bplay.c). Thanks to Pavel Kasparek for reporting it. - Several other small changes. version 1.1: - Track splitting operational - Documented signal processing and track splitting - English user interface - Removed some annoying bugs - Cleaned up source code and used indent(1) - Webpage at http://cardit.et.tudelft.nl/~card06 - Done a lot of other things version 1.0: - First `official' non-public release, at the end of the `Integraal Project Practicum', without track splitting, without full documentation (.txt files) and a Dutch user interface. gramofile-1.6/COPYING100644 1751 144 43114 6125314477 13330 0ustar costarusers 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 Appendix: 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) 19yy 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) 19yy 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. gramofile-1.6/signpr_cmf2.c100644 1751 144 60556 7070217415 14655 0ustar costarusers/* Conditional Median Filter - Better Version * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ /* Remove the `dont' to get the gate instead of the normal output - useful for verifying properties. */ #define dontVIEW_INTERNALS /* Choose the highpass filter: */ #define noSECOND_ORDER #define FOURTH_ORDER #define noSIXTH_ORDER /* To use the (slow) fancy interpolation routine, define FANCY_FILL here */ #define noFANCY_FILL #include "signpr_cmf2.h" #include "signpr_general.h" #include "signpr_l1fit.h" #include "errorwindow.h" #include "stringinput.h" #include "buttons.h" #include "clrscr.h" #include "boxes.h" #include "helpline.h" #include "yesnowindow.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif void cond_median2_param_defaults (parampointer_t parampointer) { parampointer->postlength2 = 4; parampointer->prelength2 = 4; parampointer->postlength3 = 5; parampointer->prelength3 = 5; parampointer->postlength4 = 100; parampointer->prelength4 = 100; parampointer->int1 = 12; #if defined (SECOND_ORDER) parampointer->long1 = 1000; /* Threshold to detect precise tick length. */ parampointer->long2 = 2500; /* Must be above this to be a tick. */ #elif defined (FOURTH_ORDER) parampointer->long1 = 2000; parampointer->long2 = 8500; #elif defined (SIXTH_ORDER) parampointer->long1 = 1500; parampointer->long2 = 7500; #else #error A Highpass version must be defined (signpr_cmf2.c) #endif } #ifdef FOURTH_ORDER #undef SIGNPR_CMF2_PARAMSCR_HEADERTEXT #define SIGNPR_CMF2_PARAMSCR_HEADERTEXT "CMF II [FOURTH ORDER] - Parameters" #endif #ifdef SIXTH_ORDER #undef SIGNPR_CMF2_PARAMSCR_HEADERTEXT #define SIGNPR_CMF2_PARAMSCR_HEADERTEXT "CMF II [SIXTH ORDER] - Parameters" #endif void cond_median2_param_screen (parampointer_t parampointer) { stringinput_t rmslengthstr, rmflengthstr, decimatestr, threshold1str, threshold2str; button_t ok_button, cancel_button, defaults_button; int dont_stop = TRUE; int focus = 0; int in_ch; int i; long helplong; char *helplines[8] = { " ^: less ticks detected. v: not all of tick interpolated. ", " ^: bad following of dynamics. v: less ticks detected. ", " ^: bad following of dynamics. v: less ticks detected. ", " ^: detected tick length too short v: detected tick length longer. ", " ^: only strong ticks detected. v: music-ticks also filtered out. ", " Discard changes. ", " Reset default values. ", " Accept changes. "}; rmslengthstr.maxlen = 500; rmslengthstr.string = (char *) malloc (rmslengthstr.maxlen * sizeof (char)); sprintf (rmslengthstr.string, "%ld", parampointer->prelength2 + parampointer->postlength2 + 1); rmslengthstr.y = 6; rmslengthstr.x = 59; rmslengthstr.w = 19; rmslengthstr.cursorpos = strlen (rmslengthstr.string); rmslengthstr.firstcharonscreen = 0; rmflengthstr.maxlen = 500; rmflengthstr.string = (char *) malloc (rmflengthstr.maxlen * sizeof (char)); sprintf (rmflengthstr.string, "%ld", parampointer->prelength3 + parampointer->postlength3 + 1); rmflengthstr.y = 8; rmflengthstr.x = 59; rmflengthstr.w = 19; rmflengthstr.cursorpos = strlen (rmflengthstr.string); rmflengthstr.firstcharonscreen = 0; decimatestr.maxlen = 500; decimatestr.string = (char *) malloc (decimatestr.maxlen * sizeof (char)); sprintf (decimatestr.string, "%d", parampointer->int1); decimatestr.y = 10; decimatestr.x = 59; decimatestr.w = 19; decimatestr.cursorpos = strlen (decimatestr.string); decimatestr.firstcharonscreen = 0; threshold1str.maxlen = 500; threshold1str.string = (char *) malloc (threshold1str.maxlen * sizeof (char)); sprintf (threshold1str.string, "%ld", parampointer->long1); threshold1str.y = 12; threshold1str.x = 59; threshold1str.w = 19; threshold1str.cursorpos = strlen (threshold1str.string); threshold1str.firstcharonscreen = 0; threshold2str.maxlen = 500; threshold2str.string = (char *) malloc (threshold2str.maxlen * sizeof (char)); sprintf (threshold2str.string, "%ld", parampointer->long2); threshold2str.y = 14; threshold2str.x = 59; threshold2str.w = 19; threshold2str.cursorpos = strlen (threshold2str.string); threshold2str.firstcharonscreen = 0; ok_button.text = " OK "; ok_button.y = 20; ok_button.x = 71; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; defaults_button.text = " Defaults "; defaults_button.y = 20; defaults_button.x = 36; defaults_button.selected = FALSE; clearscreen (SIGNPR_CMF2_PARAMSCR_HEADERTEXT); do { header (SIGNPR_CMF2_PARAMSCR_HEADERTEXT); if (focus == 5) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 6) defaults_button.selected = TRUE; else defaults_button.selected = FALSE; if (focus == 7) ok_button.selected = TRUE; else ok_button.selected = FALSE; mvprintw (3, 2, "See also the Signproc.txt file for the meaning of the parameters."); stringinput_display (&rmslengthstr); mvprintw (rmslengthstr.y, 2, "Length of the RMS operation (samples):"); stringinput_display (&rmflengthstr); mvprintw (rmflengthstr.y, 2, "Length of the recursive median operation (samples):"); stringinput_display (&decimatestr); mvprintw (decimatestr.y, 2, "Decimation factor for the recursive median:"); stringinput_display (&threshold1str); mvprintw (threshold1str.y, 2, "Fine threshold for tick start/end (thousandths):"); stringinput_display (&threshold2str); mvprintw (threshold2str.y, 2, "Threshold for detection of tick presence (thousandths):"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&defaults_button); mybox (defaults_button.y - 1, defaults_button.x - 1, 3, strlen (defaults_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); helpline (helplines[focus]); switch (focus) { case 0: stringinput_display (&rmslengthstr); break; case 1: stringinput_display (&rmflengthstr); break; case 2: stringinput_display (&decimatestr); break; case 3: stringinput_display (&threshold1str); break; case 4: stringinput_display (&threshold2str); break; default: move (0, 79); } refresh (); in_ch = getch (); switch (focus) { case 0: /* rmslengthstr */ stringinput_stdkeys (in_ch, &rmslengthstr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (rmslengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 1: /* rmflengthstr */ stringinput_stdkeys (in_ch, &rmflengthstr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (rmflengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 2: /* decimatestr */ stringinput_stdkeys (in_ch, &decimatestr); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (decimatestr.string, "%li", &helplong); if (i < 1 || helplong < 1) error_window ("A whole number, greater than 0, must \ be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 3: /* threshold1str */ stringinput_stdkeys (in_ch, &threshold1str); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (threshold1str.string, "%li", &helplong); if (i < 1 || helplong < 1) error_window ("A whole, positive number must be specified."); else focus++; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 4: /* threshold2str */ stringinput_stdkeys (in_ch, &threshold2str); switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (threshold2str.string, "%li", &helplong); if (i < 1 || helplong < 1) error_window ("A whole, positive number must be specified."); else focus = 7; break; case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 5: /* Cancel */ switch (in_ch) { case KEY_ENTER: case 13: dont_stop = FALSE; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 6: /* Defaults */ switch (in_ch) { case KEY_ENTER: case 13: if (yesno_window ("Restore default parameters?", " Yes ", " No ", 0)) { cond_median2_param_defaults (parampointer); dont_stop = FALSE; } break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 7: /* OK */ switch (in_ch) { case KEY_ENTER: case 13: i = sscanf (rmslengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as RMS length."); rmslengthstr.cursorpos = strlen (rmslengthstr.string); focus = 0; break; } parampointer->prelength2 = (helplong - 1) / 2; parampointer->postlength2 = (helplong - 1) / 2; i = sscanf (rmflengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as length of the recursive median."); rmflengthstr.cursorpos = strlen (rmflengthstr.string); focus = 1; break; } parampointer->prelength3 = (helplong - 1) / 2; parampointer->postlength3 = (helplong - 1) / 2; i = sscanf (decimatestr.string, "%li", &helplong); if (i < 1 || helplong < 1) { error_window ("A whole number, greater than 0, must \ be specified as decimation factor."); decimatestr.cursorpos = strlen (decimatestr.string); focus = 2; break; } parampointer->int1 = helplong; i = sscanf (threshold1str.string, "%li", &helplong); if (i < 1 || helplong < 1) { error_window ("A whole, positive number must be \ specified as threshold."); threshold1str.cursorpos = strlen (threshold1str.string); focus = 3; break; } parampointer->long1 = helplong; i = sscanf (threshold2str.string, "%li", &helplong); if (i < 1 || helplong < 1000) { error_window ("A whole, positive number must be \ specified as threshold."); threshold2str.cursorpos = strlen (threshold2str.string); focus = 4; break; } parampointer->long2 = helplong; dont_stop = FALSE; break; case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 7) focus = 0; if (focus < 0) focus = 7; } while (dont_stop); free (rmslengthstr.string); free (rmflengthstr.string); free (decimatestr.string); free (threshold1str.string); free (threshold2str.string); } void init_cond_median2_filter (int filterno, parampointer_t parampointer) { long total_post; long total_pre; long l; total_post = parampointer->postlength4 + parampointer->prelength4 + 1 + 4; /* +1+4=+5 : for highpass, max. 11th order */ total_pre = parampointer->postlength4 + parampointer->prelength4 + 1; l = parampointer->prelength4 + parampointer->prelength3 * parampointer->int1 + parampointer->prelength2 + 5; /* + 5 : for highpass, max. 11th order */ if (l > total_pre) total_pre = l; parampointer->buffer = init_buffer (total_post, total_pre); parampointer->buffer2 = init_buffer (parampointer->postlength2, parampointer->prelength2); parampointer->buffer3 = init_buffer (parampointer->postlength3, parampointer->prelength3 * parampointer->int1); parampointer->buffer4 = init_buffer (parampointer->postlength4, parampointer->prelength4); parampointer->filterno = filterno; } void delete_cond_median2_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); delete_buffer (¶mpointer->buffer2); delete_buffer (¶mpointer->buffer3); delete_buffer (¶mpointer->buffer4); } sample_t cond_median2_highpass (long offset, long offset_zero, parampointer_t parampointer) { sample_t sample; longsample_t sum; offset += offset_zero; /* middle for highpass filter in 'big buffer' */ sum.left = 0; sum.right = 0; #if defined (SECOND_ORDER) #define notTEST_DAVE_PLATT #ifndef TEST_DAVE_PLATT /* Original: */ sample = get_from_buffer (¶mpointer->buffer, offset - 1); sum.left += sample.left; sum.right += sample.right; sample = get_from_buffer (¶mpointer->buffer, offset); sum.left -= 2 * (long) sample.left; sum.right -= 2 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 1); sum.left += sample.left; sum.right += sample.right; sum.left /= 4; sum.right /= 4; #else /* TEST_DAVE_PLATT */ /* Testing, suggested by Dave Platt. Invert phase of one channel, then do tick detection using the sum signal. This is because most ticks are out-of-phase signals. I've not really tested this - it might require other settings for thresholds etc. Note: implemented for second_order only! */ sample = get_from_buffer (¶mpointer->buffer, offset - 1); sum.left += sample.left; sum.left -= sample.right; sample = get_from_buffer (¶mpointer->buffer, offset); sum.left -= 2 * (long) sample.left; sum.left += 2 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 1); sum.left += sample.left; sum.left -= sample.right; /* just in case L/R: 32000/-32000 -32000/32000 32000/-32000 : */ sum.left /= 8; sum.right = sum.left; #endif /* TEST_DAVE_PLATT */ #elif defined (FOURTH_ORDER) sample = get_from_buffer (¶mpointer->buffer, offset - 2); sum.left += sample.left; sum.right += sample.right; sample = get_from_buffer (¶mpointer->buffer, offset - 1); sum.left -= 4 * (long) sample.left; sum.right -= 4 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset); sum.left += 6 * (long) sample.left; sum.right += 6 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 1); sum.left -= 4 * (long) sample.left; sum.right -= 4 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 2); sum.left += sample.left; sum.right += sample.right; sum.left /= 4; sum.right /= 4; #elif defined (SIXTH_ORDER) sample = get_from_buffer (¶mpointer->buffer, offset - 3); sum.left -= sample.left; sum.right -= sample.right; sample = get_from_buffer (¶mpointer->buffer, offset - 2); sum.left += 6 * (long) sample.left; sum.right += 6 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset - 1); sum.left -= 15 * (long) sample.left; sum.right -= 15 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset); sum.left += 20 * (long) sample.left; sum.right += 20 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 1); sum.left -= 15 * (long) sample.left; sum.right -= 15 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 2); sum.left += 6 * (long) sample.left; sum.right += 6 * (long) sample.right; sample = get_from_buffer (¶mpointer->buffer, offset + 3); sum.left -= sample.left; sum.right -= sample.right; /* Should be /64, but the signal is extremely soft, so divide by less to get more quantization levels (more accurate) */ sum.left /= 10; sum.right /= 10; #endif if (sum.left < 32767) sample.left = sum.left; else sample.left = 32767; if (sum.right < 32767) sample.right = sum.right; else sample.right = 32767; return sample; } fillfuncpointer_t cond_median2_highpass_pointer = cond_median2_highpass; sample_t cond_median2_rms (long offset, long offset_zero, parampointer_t parampointer) { sample_t sample; doublesample_t doublesample; doublesample_t sum; long i; advance_current_pos_custom (¶mpointer->buffer2, cond_median2_highpass_pointer, offset + offset_zero, parampointer); sum.left = 0; sum.right = 0; for (i = -parampointer->postlength2; i <= parampointer->prelength2; i++) { sample = get_from_buffer (¶mpointer->buffer2, i); doublesample.left = sample.left; doublesample.right = sample.right; sum.left += doublesample.left * doublesample.left; sum.right += doublesample.right * doublesample.right; } sum.left /= (parampointer->postlength2 + parampointer->prelength2 + 1); sum.right /= (parampointer->postlength2 + parampointer->prelength2 + 1); sample.left = sqrt (sum.left + 1); sample.right = sqrt (sum.right + 1); return sample; } fillfuncpointer_t cond_median2_rms_pointer = cond_median2_rms; sample_t cond_median2_gate (long offset, long offset_zero, parampointer_t parampointer) /* Well, not a `gate' any more - just (w[t]-b[t])/b[t], comparision to make the real gate is done later. */ { sample_t sample; sample_t w_t; sample_t b_t; sample_t returnval; signed short list1[parampointer->postlength3 + parampointer->prelength3 * parampointer->int1 + 1]; signed short list2[parampointer->postlength3 + parampointer->prelength3 * parampointer->int1 + 1]; long i, j; advance_current_pos_custom (¶mpointer->buffer3, cond_median2_rms_pointer, offset + offset_zero, parampointer); w_t = get_from_buffer (¶mpointer->buffer3, 0); /* The RMF Filter */ for (i = 0; i < parampointer->postlength3; i++) { sample = get_from_buffer (¶mpointer->buffer3, i - parampointer->postlength3); list1[i] = sample.left; list2[i] = sample.right; } j = i; for (; i <= parampointer->postlength3 + parampointer->prelength3 * parampointer->int1; i += parampointer->int1) { sample = get_from_buffer (¶mpointer->buffer3, i - parampointer->postlength3); list1[j] = sample.left; list2[j] = sample.right; j++; } b_t.left = median (list1, j); b_t.right = median (list2, j); put_in_buffer (¶mpointer->buffer3, 0, b_t); i = (labs (w_t.left - b_t.left) * 1000) / b_t.left; if (i > 32767) i = 32767; returnval.left = i; i = (labs (w_t.right - b_t.right) * 1000) / b_t.right; if (i > 32767) i = 32767; returnval.right = i; return returnval; } fillfuncpointer_t cond_median2_gate_pointer = cond_median2_gate; sample_t cond_median2_filter (parampointer_t parampointer) { sample_t sample, gate, returnval; /* max. length if toleft=postlength4 and toright=prelength4 */ signed short list3[2 * (parampointer->postlength4 + parampointer->prelength4 + 1) + 1]; #ifdef FANCY_FILL signed short list4[2 * (parampointer->postlength4 + parampointer->prelength4 + 1) + 1]; double a1, b1; #endif long i; int toleft, toright; signed short maxval; advance_current_pos (¶mpointer->buffer, parampointer->filterno); advance_current_pos_custom (¶mpointer->buffer4, cond_median2_gate_pointer, 0, parampointer); gate = get_from_buffer (¶mpointer->buffer4, 0); #ifdef VIEW_INTERNALS returnval.left = 0; returnval.right = 0; #else /* 'Default' value - unchanged if there is no tick */ returnval = get_from_buffer (¶mpointer->buffer, 0); #endif if (gate.left > parampointer->long1) { maxval = gate.left; toleft = -1; sample.left = 0; do { toleft++; if (toleft < parampointer->postlength4) { sample = get_from_buffer (¶mpointer->buffer4, -toleft - 1); if (sample.left > maxval) /* if so, the `while' will continue anyway, so maxval may be adjusted here already (if necessary) */ maxval = sample.left; } } while (toleft < parampointer->postlength4 && sample.left > parampointer->long1); toright = -1; sample.left = 0; do { toright++; if (toright < parampointer->prelength4) { sample = get_from_buffer (¶mpointer->buffer4, toright + 1); if (sample.left > maxval) /* if so, the `while' will continue anyway, so maxval may be adjusted here already (if necessary) */ maxval = sample.left; } } while (toright < parampointer->prelength4 && sample.left > parampointer->long1); /* total length of gate is toleft+toright+1, so a median with size 2*(toleft+toright+1)+1 is needed. */ /* only interpolate if there really is a tick */ if (maxval > parampointer->long2) { #ifdef VIEW_INTERNALS returnval.left = (toright + toleft + 1) * 500; #else #ifdef FANCY_FILL for (i = 0; i <= 2 * (toleft + toright + 1); i++) { list3[i] = get_from_buffer (¶mpointer->buffer, i - (toleft + toright + 1)).left; list4[i] = i - (toleft + toright + 1); } l1fit (list4, list3, 2 * (toleft + toright + 1), &a1, &b1); returnval.left = (signed short) rint (a1); #else for (i = 0; i <= 2 * (toleft + toright + 1); i++) list3[i] = get_from_buffer (¶mpointer->buffer, i - (toleft + toright + 1)).left; returnval.left = median (list3, 2 * (toleft + toright + 1) + 1); #endif #endif } } if (gate.right > parampointer->long1) { maxval = gate.right; toleft = -1; sample.right = 0; do { toleft++; if (toleft < parampointer->postlength4) { sample = get_from_buffer (¶mpointer->buffer4, -toleft - 1); if (sample.right > maxval) /* if so, the `while' will continue anyway, so maxval may be adjusted here already (if necessary) */ maxval = sample.right; } } while (toleft < parampointer->postlength4 && sample.right > parampointer->long1); toright = -1; sample.right = 0; do { toright++; if (toright < parampointer->prelength4) { sample = get_from_buffer (¶mpointer->buffer4, toright + 1); if (sample.right > maxval) /* if so, the `while' will continue anyway, so maxval may be adjusted here already (if necessary) */ maxval = sample.right; } } while (toright < parampointer->prelength4 && sample.right > parampointer->long1); /* total length of gate is toleft+toright+1, so a median with size 2*(toleft+toright+1)+1 is needed. */ /* only interpolate if there really is a tick */ if (maxval > parampointer->long2) { #ifdef VIEW_INTERNALS returnval.right = (toright + toleft + 1) * 500; #else #ifdef FANCY_FILL for (i = 0; i <= 2 * (toleft + toright + 1); i++) { list3[i] = get_from_buffer (¶mpointer->buffer, i - (toleft + toright + 1)).right; list4[i] = i - (toleft + toright + 1); } l1fit (list4, list3, 2 * (toleft + toright + 1), &a1, &b1); returnval.right = (signed short) rint (a1); #else for (i = 0; i <= 2 * (toleft + toright + 1); i++) list3[i] = get_from_buffer (¶mpointer->buffer, i - (toleft + toright + 1)).right; returnval.right = median (list3, 2 * (toleft + toright + 1) + 1); #endif #endif } } return returnval; } gramofile-1.6/signpr_cmf2.h100644 1751 144 1371 7070217416 14631 0ustar costarusers/* Simple Median Filter - Better Version - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_CMF2_H #define HAVE_SIGNPR_CMF2_H #include "signpr_general.h" #define SIGNPR_CMF2_PARAMSCR_HEADERTEXT "Conditional Median Filter II - Parameters" void cond_median2_param_defaults (parampointer_t parampointer); void cond_median2_param_screen (parampointer_t parampointer); void init_cond_median2_filter (int filterno, parampointer_t parampointer); void delete_cond_median2_filter (parampointer_t parampointer); sample_t cond_median2_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_CMF2_H */ gramofile-1.6/signpr_rms.c100644 1751 144 12515 7070217416 14620 0ustar costarusers/* RMS Filter * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_rms.h" #include "signpr_general.h" #include "errorwindow.h" #include "stringinput.h" #include "buttons.h" #include "clrscr.h" #include "boxes.h" #include "helpline.h" #include #include #include #ifndef OLD_CURSES #include #else #include #endif void rms_param_defaults (parampointer_t parampointer) { parampointer->postlength1 = 1; parampointer->prelength1 = 1; } void rms_param_screen (parampointer_t parampointer) { stringinput_t rmslengthstr; button_t ok_button, cancel_button; int dont_stop = TRUE; int returnval = 0; int focus = 0; int in_ch; int i; long helplong; char *helplines[3] = { " ", " Discard changes. ", " Accept changes. "}; rmslengthstr.maxlen = 500; rmslengthstr.string = (char *) malloc (rmslengthstr.maxlen * sizeof (char)); sprintf (rmslengthstr.string, "%ld", parampointer->prelength1 + parampointer->postlength1 + 1); rmslengthstr.y = 6; rmslengthstr.x = 40; rmslengthstr.w = 15; rmslengthstr.cursorpos = strlen (rmslengthstr.string); rmslengthstr.firstcharonscreen = 0; ok_button.text = " OK "; ok_button.y = 20; ok_button.x = 71; ok_button.selected = FALSE; cancel_button.text = " Cancel "; cancel_button.y = 20; cancel_button.x = 5; cancel_button.selected = FALSE; clearscreen (SIGNPR_RMS_PARAMSCR_HEADERTEXT); do { header (SIGNPR_RMS_PARAMSCR_HEADERTEXT); if (focus == 1) cancel_button.selected = TRUE; else cancel_button.selected = FALSE; if (focus == 2) ok_button.selected = TRUE; else ok_button.selected = FALSE; /* mvprintw (3, 2, "See the Signproc.txt file for the meaning of the parameters."); */ stringinput_display (&rmslengthstr); mvprintw (rmslengthstr.y, 2, "Number of samples to compute RMS of:"); button_display (&cancel_button); mybox (cancel_button.y - 1, cancel_button.x - 1, 3, strlen (cancel_button.text) + 2); button_display (&ok_button); mybox (ok_button.y - 1, ok_button.x - 1, 3, strlen (ok_button.text) + 2); helpline (helplines[focus]); if (focus == 0) stringinput_display (&rmslengthstr); else move (0, 79); refresh (); in_ch = getch (); switch (focus) { case 0: /* rmslengthstr */ stringinput_stdkeys (in_ch, &rmslengthstr); if (in_ch == KEY_ENTER || in_ch == 13) { i = sscanf (rmslengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) error_window ("A whole, odd number, greater than 0, must \ be specified."); else focus = 2; } else switch (in_ch) { case KEY_UP: focus--; break; case KEY_DOWN: focus++; break; } break; case 1: /* Cancel */ if (in_ch == KEY_ENTER || in_ch == 13) { returnval = 0; dont_stop = FALSE; } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; case 2: /* OK */ if (in_ch == KEY_ENTER || in_ch == 13) { i = sscanf (rmslengthstr.string, "%li", &helplong); if (i < 1 || helplong < 1 || helplong % 2 == 0) { error_window ("A whole, odd number, greater than 0, must \ be specified as RMS length."); rmslengthstr.cursorpos = strlen (rmslengthstr.string); focus = 0; } else { parampointer->prelength1 = (helplong - 1) / 2; parampointer->postlength1 = (helplong - 1) / 2; dont_stop = FALSE; } } else switch (in_ch) { case KEY_LEFT: case KEY_UP: focus--; break; case KEY_RIGHT: case KEY_DOWN: focus++; break; } break; } if (in_ch == 9) /* TAB */ focus++; if (in_ch == 27) dont_stop = FALSE; if (focus > 2) focus -= 3; if (focus < 0) focus += 3; } while (dont_stop); free (rmslengthstr.string); } void init_rms_filter (int filterno, parampointer_t parampointer) { parampointer->buffer = init_buffer (parampointer->postlength1, parampointer->prelength1); parampointer->filterno = filterno; } void delete_rms_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); } sample_t rms_filter (parampointer_t parampointer) { doublesample_t sum; sample_t sample; long i; advance_current_pos (¶mpointer->buffer, parampointer->filterno); sum.left = 0; sum.right = 0; for (i = 0; i <= parampointer->postlength1 + parampointer->prelength1; i++) { sample = get_from_buffer (¶mpointer->buffer, i - parampointer->postlength1); sum.left += sample.left * sample.left; sum.right += sample.right * sample.right; } sample.left = sqrt (sum.left / (parampointer->postlength1 + parampointer->prelength1 + 1)); sample.right = sqrt (sum.right / (parampointer->postlength1 + parampointer->prelength1 + 1)); return sample; } gramofile-1.6/signpr_rms.h100644 1751 144 1233 7070217416 14600 0ustar costarusers/* RMS Filter - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_RMS_H #define HAVE_SIGNPR_RMS_H #include "signpr_general.h" #define SIGNPR_RMS_PARAMSCR_HEADERTEXT "RMS Filter - Parameters" void rms_param_defaults (parampointer_t parampointer); void rms_param_screen (parampointer_t parampointer); void init_rms_filter (int filterno, parampointer_t parampointer); void delete_rms_filter (parampointer_t parampointer); sample_t rms_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_RMS_H */ gramofile-1.6/endian.c100644 1751 144 1612 7070217415 13646 0ustar costarusers/* Stolen from examples on the web */ /* Joshua Weage gte855f@prism.gatech.edu */ /* Switches endianness */ #include #include "signpr_general.h" #include "endian.h" u_short SwapTwoBytes (u_short w) { register u_short tmp; tmp = (w & 0x00FF); tmp = ((w & 0xFF00) >> 0x08) | (tmp << 0x08); return (tmp); } short SwapTwo (short w) { register short tmp; tmp = (w & 0x00FF); tmp = ((w & 0xFF00) >> 0x08) | (tmp << 0x08); return (tmp); } u_long SwapFourBytes (u_long dw) { register u_long tmp; tmp = (dw & 0x000000FF); tmp = ((dw & 0x0000FF00) >> 0x08) | (tmp << 0x08); tmp = ((dw & 0x00FF0000) >> 0x10) | (tmp << 0x08); tmp = ((dw & 0xFF000000) >> 0x18) | (tmp << 0x08); return (tmp); } sample_t SwapSample (sample_t sample) { sample.left = SwapTwo (sample.left); sample.right = SwapTwo (sample.right); return (sample); } gramofile-1.6/endian.h100644 1751 144 371 7070217416 13635 0ustar costarusers/* Header file for endian.c */ /* Joshua Weage gte855f@prism.gatech.edu */ #ifndef _GETBIG #define _GETBIG 1 extern u_short SwapTwoBytes (u_short); extern u_long SwapFourBytes (u_long); extern sample_t SwapSample (sample_t); #endif gramofile-1.6/signpr_copy.c100644 1751 144 1704 7070217415 14746 0ustar costarusers/* Copy Only `Filter' * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_copy.h" #include "signpr_general.h" #include "errorwindow.h" void copyonly_param_defaults (parampointer_t parampointer) { } void copyonly_param_screen (parampointer_t parampointer) { error_window ("This `filter' does not have adjustable parameters. It \ just copies the signal unchanged."); } void init_copyonly_filter (int filterno, parampointer_t parampointer) { parampointer->buffer = init_buffer (0, 0); parampointer->filterno = filterno; } void delete_copyonly_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); } sample_t copyonly_filter (parampointer_t parampointer) { advance_current_pos (¶mpointer->buffer, parampointer->filterno); return get_from_buffer (¶mpointer->buffer, 0); } gramofile-1.6/signpr_copy.h100644 1751 144 1175 7070217416 14756 0ustar costarusers/* Copy Only `Filter' - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_COPY_H #define HAVE_SIGNPR_COPY_H #include "signpr_general.h" void copyonly_param_defaults (parampointer_t parampointer); void copyonly_param_screen (parampointer_t parampointer); void init_copyonly_filter (int filterno, parampointer_t parampointer); void delete_copyonly_filter (parampointer_t parampointer); sample_t copyonly_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_COPY_H */ gramofile-1.6/signpr_exper.c100644 1751 144 4400 7070217415 15113 0ustar costarusers/* Experimenting Filter * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ /* You can experiment with this filter to your hearts desire. Currently, samples x[t-100] through x[t+100] are accessible. An example smoothing filter is presented below, so you can get an idea how to program things. */ #include "signpr_exper.h" #include "signpr_general.h" #include "errorwindow.h" #include void experiment_param_defaults (parampointer_t parampointer) { } void experiment_param_screen (parampointer_t parampointer) { error_window ("This is a filter with which you can experiment. The \ source code is in signpr_exper.c"); } void init_experiment_filter (int filterno, parampointer_t parampointer) { parampointer->buffer = init_buffer (100, 100); parampointer->filterno = filterno; } void delete_experiment_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); } sample_t experiment_filter (parampointer_t parampointer) { sample_t sample; longsample_t longsample; /* doublesample_t doublesample; */ advance_current_pos (¶mpointer->buffer, parampointer->filterno); /* Example: a smoothing filter (lowpass, that is): y[t] = { x[t-2] + 5*x[t-1] + 13*x[t] + 5*x[t+1] + x[t+2] } / 25 */ /* zero totals */ longsample.left = 0; longsample.right = 0; /* compute the weighted sum */ sample = get_from_buffer (¶mpointer->buffer, -2); longsample.left += sample.left; longsample.right += sample.right; sample = get_from_buffer (¶mpointer->buffer, -1); longsample.left += 5 * sample.left; longsample.right += 5 * sample.right; sample = get_from_buffer (¶mpointer->buffer, 0); longsample.left += 13 * sample.left; longsample.right += 13 * sample.right; sample = get_from_buffer (¶mpointer->buffer, 1); longsample.left += 5 * sample.left; longsample.right += 5 * sample.right; sample = get_from_buffer (¶mpointer->buffer, 2); longsample.left += sample.left; longsample.right += sample.right; /* devide by the total weight */ sample.left = longsample.left / 25; sample.right = longsample.right / 25; /* return the computed sample */ return sample; } gramofile-1.6/signpr_exper.h100644 1751 144 1211 7070217416 15116 0ustar costarusers/* Experiment Filter - Header * Copyright (C) 1998 J.A. Bezemer * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_EXPER_H #define HAVE_SIGNPR_EXPER_H #include "signpr_general.h" void experiment_param_defaults (parampointer_t parampointer); void experiment_param_screen (parampointer_t parampointer); void init_experiment_filter (int filterno, parampointer_t parampointer); void delete_experiment_filter (parampointer_t parampointer); sample_t experiment_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_EXPER_H */ gramofile-1.6/Tracksplit2.txt100644 1751 144 15067 6662012624 15241 0ustar costarusers In version 1.4 of GramoFile, a entirely new track location algorithm is implemented, which will be briefly discussed here. The various values mentioned are the defaults offered in GramoFile 1.4 (and higher). Also, a procedure is described that you may follow to get more or less optimal track location. TRACK LOCATION First of all, a `power profile' of the sound file is created, by taking the Root-Mean-Square (`RMS') of 4410 samples of the signal. We get something like: | | | || || || ||| || | || | | || ||||||| |||||||||||||| ||| |||||| ||||| |||||||||||| ||||||||||||||||| |||||||||| |||||||||||||||||| ||||||||||||||||| ||||||||||| ||||||||||||||||||| |||||||||||||||||| |||||||||||| ||||||||||||||||||||| |||||||||||||||||||| ||||||||||||| ||||||||||||||||||||||| ||||||||||||||||||||||| |||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ^ ^ ^ ^ ^ ^ ^ silence track silence track silence track silence The RMS-computation takes quite some time. Therefore the (binary)RMS-data may/will be saved to a `.rms' file. This .rms file contains all information necessary to locate tracks (the .wav file is not needed any more). To beautify the data a little, a running median (length 3) is applied. The above graph gets a little smoother by doing this. The result of this operation may be saved to a .med `graph file', that shows a rotated (and much longer) version of the graph above. Then, a first coarse track location will be applied. This is simply walking through the `.med data' and finding sequences that are either above (= possible track) or below (= possible silence) a certain threshold, the `global silence threshold'. This global silence threshold is different for each sound file. To `automatically' find a reasonable threshold, first a minimum threshold and a maximum threshold are computed. The minimum threshold is (about) the lowest `.med value' in the data set. The maximum threshold is the middle `.med value' (that's usually very close to the mean `.med value'). To find these, the `.med data' is sorted, and we get something like this: min. thresh. max. thresh. | v v || |||| |||||| |||||||||| |||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ^ ^ collected silence music The minimum silence now is the 10th element in the sorted data, and the maximum threshold is the middle element. The sorted data may also be saved to a .sor `graph file'. When these min and max thresholds are found, the real global silence threshold is found by taking a value between the min anc max threshold. Default, that value lies on 15% (= 150 * 0.1%, which seems to work well in most cases), as shown below: value `Global silence factor' in Parameter-screen | V maximum threshold 1000 100% -- -- -- -- -- -- -- -> global_silence_threshold 150 15% -- -- minimum threshold 0 0% -- When the possible tracks and silences have been located, they are `transformed' into certain tracks / silences by applying a few `rules'. The most important one is that possible tracks longer than 50 blocks (= 5 seconds when using 4410-sample blocks) are certain tracks, and possible silences longer than 20 blocks (= 2 seconds) are certain silences. For more information, see the funtion tracksplit_findtracks() in the file tracksplit.c. To detect the track starts/ends more correctly, new local thresholds are computed, by taking the mean of the .med's within the individual silences and adding 5% on top of that (the `Local silence factor'). The tracks adjecent to a silence are now extended using the local threshold. Finally the detected `certain tracks' are extended a bit by adding 3 blocks (0.3 secs) before and 6 blocks (0.6 secs) after the detected track. This is to account for fade-in/fade-out effects, that are hard to detect. When all this is done, track location is completed. The detected location of the tracks is written to the .tracks file. PROCEDURE The best way to use track location is this: - First, try track location with the default parameters. - If too few tracks are detected, check if the `minimal length of inter- track silences' is correct. Some records require 10 (blocks). - If inter-track silence was not the problem, try to increase the `global silence factor' to for example 250 or 350. Really weird recordings may need even higher values, but 1000 should really be enough. - It is generally _no_ problem if 2 or 3 tracks too much are detected. - When you're satisfied with the automatic detection, you may fine-tune/ adjust the starts/ends manually, using a text editor and the `Play'- `Track' option. You may have to join some tracks, but that won't be too difficult. If you encounter a situation in which the track splitting (still) doesn't function properly, you can contact me about it. You may include the .rms file of the .wav you're having problems with, and describe your problem as clearly and completely as possible. Also, corrections, additions, suggestions and compliments are heartily welcomed. Contact information is in the general `README' file. gramofile-1.6/sun_scandir.c100644 1751 144 4307 7070217416 14725 0ustar costarusers/******************************************************************************* * * * Viewmol * * * * S C A N D I R . C * * * * Copyright (c) Joerg-R. Hill, May 1999 * * * ******************************************************************************** * * $Id: scandir.c,v 1.1 1999/05/24 01:29:43 jrh Exp $ * $Log: scandir.c,v $ * Revision 1.1 1999/05/24 01:29:43 jrh * Initial revision * */ #include #include #include #include /* This function is only required for SunOS, all other supported OS have this function in their system library */ int scandir (const char *dir, struct dirent ***namelist, int (*select) (const struct dirent *), int (*compar) (const struct dirent **, const struct dirent **)) { DIR *d; struct dirent *entry; register int i = 0; size_t entrysize; if ((d = opendir (dir)) == NULL) return (-1); *namelist = NULL; while ((entry = readdir (d)) != NULL) { if (select == NULL || (select != NULL && (*select) (entry))) { *namelist = (struct dirent **) realloc ((void *) (*namelist), (size_t) ((i + 1) * sizeof (struct dirent *))); if (*namelist == NULL) return (-1); entrysize = sizeof (struct dirent) - sizeof (entry->d_name) + strlen (entry->d_na me) + 1; (*namelist)[i] = (struct dirent *) malloc (entrysize); if ((*namelist)[i] == NULL) return (-1); memcpy ((*namelist)[i], entry, entrysize); i++; } } if (closedir (d)) return (-1); if (i == 0) return (-1); if (compar != NULL) qsort ((void *) (*namelist), (size_t) i, sizeof (struct dirent *), compar); return (i); } int alphasort (const struct dirent **a, const struct dirent **b) { return (strcmp ((*a)->d_name, (*b)->d_name)); } gramofile-1.6/signpr_mono.c100644 1751 144 2243 7070217416 14744 0ustar costarusers/* Convert to mono `Filter' * Copyright (C) 1999 S.J. Tappin * Derived from the Copy only filter (J.A. Bezemer 1998) * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #include "signpr_mono.h" #include "signpr_general.h" #include "errorwindow.h" void monoize_param_defaults (parampointer_t parampointer) { } void monoize_param_screen (parampointer_t parampointer) { error_window ("This `filter' does not have adjustable parameters. It \ just averages the left & right signals."); } void init_monoize_filter (int filterno, parampointer_t parampointer) { parampointer->buffer = init_buffer (0, 0); parampointer->filterno = filterno; } void delete_monoize_filter (parampointer_t parampointer) { delete_buffer (¶mpointer->buffer); } sample_t monoize_filter (parampointer_t parampointer) { sample_t sample; longsample_t sum; advance_current_pos (¶mpointer->buffer, parampointer->filterno); sample = get_from_buffer (¶mpointer->buffer, 0); sum.left = (sample.left + sample.right) / 2; sample.left = sum.left; sample.right = sum.left; return sample; } gramofile-1.6/signpr_mono.h100644 1751 144 1251 7070217416 14747 0ustar costarusers/* Convert to mono `Filter' - Header * Copyright (C) 1999 S.J. Tappin * (After signpr_copy.h: J.A. Bezemer 1998) * * Licensed under the terms of the GNU General Public License. * ABSOLUTELY NO WARRANTY. * See the file `COPYING' in this directory. */ #ifndef HAVE_SIGNPR_MONO_H #define HAVE_SIGNPR_MONO_H #include "signpr_general.h" void monoize_param_defaults (parampointer_t parampointer); void monoize_param_screen (parampointer_t parampointer); void init_monoize_filter (int filterno, parampointer_t parampointer); void delete_monoize_filter (parampointer_t parampointer); sample_t monoize_filter (parampointer_t parampointer); #endif /* HAVE_SIGNPR_MONO_H */ gramofile-1.6/signpr_l1fit.c100644 1751 144 5002 7070217416 15007 0ustar costarusers/* signpr_l1fit.c Does L1 norm fit to supplied data, using integer arithmetic. Copyright (C) 1999 S.J. Tappin Licensed under the terms of the GNU General Public License. ABSOLUTELY NO WARRANTY. See the file `COPYING' in this directory. */ #include #include #include "signpr_general.h" #include "signpr_l1fit.h" long mdfunc (b, x, y, a, n, tmp1) double b, *a; signed short *x, *y, *tmp1; int n; { int i; long result; double dtmp; for (i = 0; i < n; i++) { tmp1[i] = (signed short) rint (y[i] - b * x[i]); } *a = (double) median (tmp1, n); result = 0; for (i = 0; i < n; i++) { dtmp = (y[i] - (b * x[i] + *a)); if (y[i] != 0) dtmp = dtmp / abs (y[i]); if (dtmp > 0) { result += x[i]; } else if (dtmp < 0) { result -= x[i]; } } return (result); } void l1fit (x, y, n, a, b) signed short *x, *y; double *a, *b; int n; { int sx, sy, sxx, sxy; int del; double aa, bb, c2, sigb; double b1, b2, delb; long f1, f2, f; int i; signed short *tmp1; sx = 0; sy = 0; sxx = 0; sxy = 0; for (i = 0; i < n; i++) { sx += x[i]; sy += y[i]; sxx += x[i] * x[i]; sxy += x[i] * y[i]; } del = n * sxx - sx * sx; if (del == 0) { /* All X's the same only fit horizontal */ *a = (double) median (y, n); *b = 0.; return; } /* Use the normal least squares (L2) fit as a starting point. */ aa = (sxx * sy - sx * sxy) / (double) del; bb = (n * sxy - sx * sy) / (double) del; c2 = 0.; for (i = 0; i < n; i++) c2 += (y[i] - (aa + bb * x[i])) * (y[i] - (aa + bb * x[i])); sigb = sqrt (c2 / del); /* Perfect fit, L1 must give the same answer so go straight back */ if (c2 == 0.) { *a = aa; *b = bb; return; } tmp1 = malloc (n * sizeof (short)); b1 = bb; f1 = mdfunc (b1, x, y, &aa, n, tmp1); if (f1 >= 0) { delb = sigb * 3.; } else { delb = -sigb * 3.; } b2 = b1 + delb; f2 = mdfunc (b2, x, y, &aa, n, tmp1); while (f1 * f2 > 0) { b1 = b2; f1 = f2; b2 = b1 + delb; f2 = mdfunc (b2, x, y, &aa, n, tmp1); } sigb *= 0.01; while (fabs (b1 - b2) > sigb) { bb = (b1 + b2) / 2.; if (bb == b1 || bb == b2) break; f = mdfunc (bb, x, y, &aa, n, tmp1); if (f * f1 >= 0) { f1 = f; b1 = bb; } else { f2 = f; b2 = bb; } } free (tmp1); *a = aa; *b = bb; } gramofile-1.6/signpr_l1fit.h100644 1751 144 336 7070217416 15001 0ustar costaruserslong mdfunc (double b, signed short *x, signed short *y, double *a, int n, signed short *tmp); void l1fit (signed short *x, signed short *y, int n, double *a, double *b);