├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── README.md ├── TODO ├── autogen.sh ├── configure.in ├── pixmaps ├── Makefile.am ├── busy.png ├── off-icon.png ├── off.png ├── on-icon.png ├── on.png └── timemachine-icon.png └── src ├── .gitignore ├── Makefile.am ├── callbacks.c ├── callbacks.h ├── gtkmeter.c ├── gtkmeter.h ├── gtkmeterscale.c ├── gtkmeterscale.h ├── interface.c ├── interface.h ├── main.c ├── main.h ├── meters.c ├── meters.h ├── support.c ├── support.h ├── threads.c └── threads.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | Makefile 3 | Makefile.in 4 | *.m4 5 | config.* 6 | install-sh 7 | autom4te.cache 8 | configure 9 | depcomp 10 | missing 11 | stamp-h1 12 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/AUTHORS -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2005-09-19 Steve Harris 2 | 3 | * main.c: Change LADCCA suport to LASH support, add code to send name 4 | to LASH server. 5 | 6 | 2005-01-22 Steve Harris 7 | 8 | * gtkmeter.c: fixed incorrect constant in gtkmeter.c that caused the 9 | meter to jump between -50 and -60 dB's. 10 | 11 | release 0.3.0 12 | 13 | 2004-10-28 Steve Harris 14 | 15 | * main.c: made commandline mode trigerable from the command line, and a 16 | bit less fussy about what consitiutes DISPLAY bieng unset. Improved 17 | help information. 18 | 19 | 2004-09-13 Steve Harris 20 | 21 | * main.c: added commandline mode to run without X11 (from Mario Lang). 22 | 23 | release 0.2.5 24 | 25 | 2004-09-07 Steve Harris 26 | 27 | * main.c: added format option, added port command line names connection 28 | 29 | 2004-07-22 Steve Harris 30 | 31 | * main.c: applied patch from Joern Nettingsmeier to make building 32 | without LADCCA work. 33 | 34 | 2004-03-03 Steve Harris 35 | 36 | * main.c, callbacks.c: added icons for on and off states 37 | 38 | 2004-03-03 Steve Harris 39 | 40 | * main.c: Finished off LADCCA support, added quit handling, added 41 | DEBUG macro, minor cleanups. 42 | 43 | 2004-03-02 Steve Harris 44 | 45 | * interface.c: fixed bug that prevented running with 1 channel 46 | 47 | 2004-02-20 Steve Harris 48 | 49 | * configure.in: added test for LADCCA (from rah) 50 | 51 | * main.c: added LADCCA initialisation code 52 | 53 | 2004-02-18 Steve Harris 54 | 55 | * interface.c: changed the button to a gtk_button 56 | 57 | * threads.c: made the button dead when theres stuff to be written 58 | out to disk from the pre-buffer 59 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software 2 | Foundation, Inc. 3 | 4 | This file is free documentation; the Free Software Foundation gives 5 | unlimited permission to copy, distribute and modify it. 6 | 7 | Basic Installation 8 | ================== 9 | 10 | These are generic installation instructions. 11 | 12 | The `configure' shell script attempts to guess correct values for 13 | various system-dependent variables used during compilation. It uses 14 | those values to create a `Makefile' in each directory of the package. 15 | It may also create one or more `.h' files containing system-dependent 16 | definitions. Finally, it creates a shell script `config.status' that 17 | you can run in the future to recreate the current configuration, and a 18 | file `config.log' containing compiler output (useful mainly for 19 | debugging `configure'). 20 | 21 | It can also use an optional file (typically called `config.cache' 22 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 23 | the results of its tests to speed up reconfiguring. (Caching is 24 | disabled by default to prevent problems with accidental use of stale 25 | cache files.) 26 | 27 | If you need to do unusual things to compile the package, please try 28 | to figure out how `configure' could check whether to do them, and mail 29 | diffs or instructions to the address given in the `README' so they can 30 | be considered for the next release. If you are using the cache, and at 31 | some point `config.cache' contains results you don't want to keep, you 32 | may remove or edit it. 33 | 34 | The file `configure.ac' (or `configure.in') is used to create 35 | `configure' by a program called `autoconf'. You only need 36 | `configure.ac' if you want to change it or regenerate `configure' using 37 | a newer version of `autoconf'. 38 | 39 | The simplest way to compile this package is: 40 | 41 | 1. `cd' to the directory containing the package's source code and type 42 | `./configure' to configure the package for your system. If you're 43 | using `csh' on an old version of System V, you might need to type 44 | `sh ./configure' instead to prevent `csh' from trying to execute 45 | `configure' itself. 46 | 47 | Running `configure' takes awhile. While running, it prints some 48 | messages telling which features it is checking for. 49 | 50 | 2. Type `make' to compile the package. 51 | 52 | 3. Optionally, type `make check' to run any self-tests that come with 53 | the package. 54 | 55 | 4. Type `make install' to install the programs and any data files and 56 | documentation. 57 | 58 | 5. You can remove the program binaries and object files from the 59 | source code directory by typing `make clean'. To also remove the 60 | files that `configure' created (so you can compile the package for 61 | a different kind of computer), type `make distclean'. There is 62 | also a `make maintainer-clean' target, but that is intended mainly 63 | for the package's developers. If you use it, you may have to get 64 | all sorts of other programs in order to regenerate files that came 65 | with the distribution. 66 | 67 | Compilers and Options 68 | ===================== 69 | 70 | Some systems require unusual options for compilation or linking that 71 | the `configure' script does not know about. Run `./configure --help' 72 | for details on some of the pertinent environment variables. 73 | 74 | You can give `configure' initial values for configuration parameters 75 | by setting variables in the command line or in the environment. Here 76 | is an example: 77 | 78 | ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix 79 | 80 | *Note Defining Variables::, for more details. 81 | 82 | Compiling For Multiple Architectures 83 | ==================================== 84 | 85 | You can compile the package for more than one kind of computer at the 86 | same time, by placing the object files for each architecture in their 87 | own directory. To do this, you must use a version of `make' that 88 | supports the `VPATH' variable, such as GNU `make'. `cd' to the 89 | directory where you want the object files and executables to go and run 90 | the `configure' script. `configure' automatically checks for the 91 | source code in the directory that `configure' is in and in `..'. 92 | 93 | If you have to use a `make' that does not support the `VPATH' 94 | variable, you have to compile the package for one architecture at a 95 | time in the source code directory. After you have installed the 96 | package for one architecture, use `make distclean' before reconfiguring 97 | for another architecture. 98 | 99 | Installation Names 100 | ================== 101 | 102 | By default, `make install' will install the package's files in 103 | `/usr/local/bin', `/usr/local/man', etc. You can specify an 104 | installation prefix other than `/usr/local' by giving `configure' the 105 | option `--prefix=PATH'. 106 | 107 | You can specify separate installation prefixes for 108 | architecture-specific files and architecture-independent files. If you 109 | give `configure' the option `--exec-prefix=PATH', the package will use 110 | PATH as the prefix for installing programs and libraries. 111 | Documentation and other data files will still use the regular prefix. 112 | 113 | In addition, if you use an unusual directory layout you can give 114 | options like `--bindir=PATH' to specify different values for particular 115 | kinds of files. Run `configure --help' for a list of the directories 116 | you can set and what kinds of files go in them. 117 | 118 | If the package supports it, you can cause programs to be installed 119 | with an extra prefix or suffix on their names by giving `configure' the 120 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 121 | 122 | Optional Features 123 | ================= 124 | 125 | Some packages pay attention to `--enable-FEATURE' options to 126 | `configure', where FEATURE indicates an optional part of the package. 127 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 128 | is something like `gnu-as' or `x' (for the X Window System). The 129 | `README' should mention any `--enable-' and `--with-' options that the 130 | package recognizes. 131 | 132 | For packages that use the X Window System, `configure' can usually 133 | find the X include and library files automatically, but if it doesn't, 134 | you can use the `configure' options `--x-includes=DIR' and 135 | `--x-libraries=DIR' to specify their locations. 136 | 137 | Specifying the System Type 138 | ========================== 139 | 140 | There may be some features `configure' cannot figure out 141 | automatically, but needs to determine by the type of machine the package 142 | will run on. Usually, assuming the package is built to be run on the 143 | _same_ architectures, `configure' can figure that out, but if it prints 144 | a message saying it cannot guess the machine type, give it the 145 | `--build=TYPE' option. TYPE can either be a short name for the system 146 | type, such as `sun4', or a canonical name which has the form: 147 | 148 | CPU-COMPANY-SYSTEM 149 | 150 | where SYSTEM can have one of these forms: 151 | 152 | OS KERNEL-OS 153 | 154 | See the file `config.sub' for the possible values of each field. If 155 | `config.sub' isn't included in this package, then this package doesn't 156 | need to know the machine type. 157 | 158 | If you are _building_ compiler tools for cross-compiling, you should 159 | use the `--target=TYPE' option to select the type of system they will 160 | produce code for. 161 | 162 | If you want to _use_ a cross compiler, that generates code for a 163 | platform different from the build platform, you should specify the 164 | "host" platform (i.e., that on which the generated programs will 165 | eventually be run) with `--host=TYPE'. 166 | 167 | Sharing Defaults 168 | ================ 169 | 170 | If you want to set default values for `configure' scripts to share, 171 | you can create a site shell script called `config.site' that gives 172 | default values for variables like `CC', `cache_file', and `prefix'. 173 | `configure' looks for `PREFIX/share/config.site' if it exists, then 174 | `PREFIX/etc/config.site' if it exists. Or, you can set the 175 | `CONFIG_SITE' environment variable to the location of the site script. 176 | A warning: not all `configure' scripts look for a site script. 177 | 178 | Defining Variables 179 | ================== 180 | 181 | Variables not defined in a site shell script can be set in the 182 | environment passed to `configure'. However, some packages may run 183 | configure again during the build, and the customized values of these 184 | variables may be lost. In order to avoid this problem, you should set 185 | them in the `configure' command line, using `VAR=value'. For example: 186 | 187 | ./configure CC=/usr/local2/bin/gcc 188 | 189 | will cause the specified gcc to be used as the C compiler (unless it is 190 | overridden in the site shell script). 191 | 192 | `configure' Invocation 193 | ====================== 194 | 195 | `configure' recognizes the following options to control how it 196 | operates. 197 | 198 | `--help' 199 | `-h' 200 | Print a summary of the options to `configure', and exit. 201 | 202 | `--version' 203 | `-V' 204 | Print the version of Autoconf used to generate the `configure' 205 | script, and exit. 206 | 207 | `--cache-file=FILE' 208 | Enable the cache: use and save the results of the tests in FILE, 209 | traditionally `config.cache'. FILE defaults to `/dev/null' to 210 | disable caching. 211 | 212 | `--config-cache' 213 | `-C' 214 | Alias for `--cache-file=config.cache'. 215 | 216 | `--quiet' 217 | `--silent' 218 | `-q' 219 | Do not print messages saying which checks are being made. To 220 | suppress all normal output, redirect it to `/dev/null' (any error 221 | messages will still be shown). 222 | 223 | `--srcdir=DIR' 224 | Look for the package's source code in directory DIR. Usually 225 | `configure' can determine that directory automatically. 226 | 227 | `configure' also accepts some other, not widely useful, options. Run 228 | `configure --help' for more details. 229 | 230 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | SUBDIRS = src pixmaps 4 | 5 | EXTRA_DIST = autogen.sh 6 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/NEWS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TimeMachine 2 | 3 | I used to always keep a minidisc recorder in my studio running in a mode where 4 | when you pressed record it wrote the last 10 seconds of audio to the disk and 5 | then caught up to realtime and kept recording. The recorder died and haven't 6 | been able to replace it, so this is a simple jack app to do the same job. It 7 | has the advantage that it never clips and can be wired to any part of the jack 8 | graph. 9 | 10 | The idea is that I doodle away with whatever is kicking around in my studio 11 | and when I heard an interesting noise, I'd press record and capture it, 12 | without having to try and recreate it. :) 13 | 14 | ## Building 15 | 16 | This app requires: 17 | 18 | * JACK 19 | * GTK+ 2.x 20 | * libsndfile 21 | 22 | and optionally: 23 | 24 | * LASH 25 | * liblo 26 | 27 | You will also need the -devel packages if you're using packages. 28 | 29 | ./configure 30 | make 31 | su -c "make install" 32 | 33 | ## Usage 34 | 35 | Run it with "timemachine" then connect it up with a jack patchbay app. To 36 | start recording click in the window, to stop recording click in the window 37 | again. 38 | 39 | It will create a file following tm-*.wav, with an ISO 8601 timestamp, eg 40 | tm-2003-01-19T20:47:03.wav. The time is the time that the recording starts 41 | from, not when you click. 42 | 43 | ## OSC 44 | 45 | If you compiled with liblo, TimeMachine supports starting stopping via OSC, 46 | triggered by sending a message with the path `/start` or `/stop` and no 47 | arguments. 48 | 49 | Example Python code: 50 | 51 | import liblo 52 | liblo.send(('localhost',7133), '/start') 53 | liblo.send(('localhost',7133), '/stop') 54 | 55 | ## Contact 56 | 57 | You can report bugs through github at https://github.com/swh/timemachine 58 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Add running time timer / disk space left indicator 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run this to generate all the initial makefiles, etc. 3 | 4 | srcdir=`dirname $0` 5 | test -z "$srcdir" && srcdir=. 6 | 7 | DIE=0 8 | 9 | if [ -n "$GNOME2_DIR" ]; then 10 | ACLOCAL_FLAGS="-I $GNOME2_DIR/share/aclocal $ACLOCAL_FLAGS" 11 | LD_LIBRARY_PATH="$GNOME2_DIR/lib:$LD_LIBRARY_PATH" 12 | PATH="$GNOME2_DIR/bin:$PATH" 13 | export PATH 14 | export LD_LIBRARY_PATH 15 | fi 16 | 17 | (test -f $srcdir/configure.in) || { 18 | echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" 19 | echo " top-level package directory" 20 | exit 1 21 | } 22 | 23 | (autoconf --version) < /dev/null > /dev/null 2>&1 || { 24 | echo 25 | echo "**Error**: You must have \`autoconf' installed." 26 | echo "Download the appropriate package for your distribution," 27 | echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" 28 | DIE=1 29 | } 30 | 31 | (grep "^AC_PROG_INTLTOOL" $srcdir/configure.in >/dev/null) && { 32 | (intltoolize --version) < /dev/null > /dev/null 2>&1 || { 33 | echo 34 | echo "**Error**: You must have \`intltool' installed." 35 | echo "You can get it from:" 36 | echo " ftp://ftp.gnome.org/pub/GNOME/" 37 | DIE=1 38 | } 39 | } 40 | 41 | (grep "^AM_PROG_XML_I18N_TOOLS" $srcdir/configure.in >/dev/null) && { 42 | (xml-i18n-toolize --version) < /dev/null > /dev/null 2>&1 || { 43 | echo 44 | echo "**Error**: You must have \`xml-i18n-toolize' installed." 45 | echo "You can get it from:" 46 | echo " ftp://ftp.gnome.org/pub/GNOME/" 47 | DIE=1 48 | } 49 | } 50 | 51 | (grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && { 52 | (libtool --version) < /dev/null > /dev/null 2>&1 || { 53 | echo 54 | echo "**Error**: You must have \`libtool' installed." 55 | echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" 56 | DIE=1 57 | } 58 | } 59 | 60 | (grep "^AM_GLIB_GNU_GETTEXT" $srcdir/configure.in >/dev/null) && { 61 | (grep "sed.*POTFILES" $srcdir/configure.in) > /dev/null || \ 62 | (glib-gettextize --version) < /dev/null > /dev/null 2>&1 || { 63 | echo 64 | echo "**Error**: You must have \`glib' installed." 65 | echo "You can get it from: ftp://ftp.gtk.org/pub/gtk" 66 | DIE=1 67 | } 68 | } 69 | 70 | (automake --version) < /dev/null > /dev/null 2>&1 || { 71 | echo 72 | echo "**Error**: You must have \`automake' installed." 73 | echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" 74 | DIE=1 75 | NO_AUTOMAKE=yes 76 | } 77 | 78 | 79 | # if no automake, don't bother testing for aclocal 80 | test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { 81 | echo 82 | echo "**Error**: Missing \`aclocal'. The version of \`automake'" 83 | echo "installed doesn't appear recent enough." 84 | echo "You can get automake from ftp://ftp.gnu.org/pub/gnu/" 85 | DIE=1 86 | } 87 | 88 | if test "$DIE" -eq 1; then 89 | exit 1 90 | fi 91 | 92 | if test -z "$*"; then 93 | echo "**Warning**: I am going to run \`configure' with no arguments." 94 | echo "If you wish to pass any to it, please specify them on the" 95 | echo \`$0\'" command line." 96 | echo 97 | fi 98 | 99 | case $CC in 100 | xlc ) 101 | am_opt=--include-deps;; 102 | esac 103 | 104 | for coin in `find $srcdir -path $srcdir/CVS -prune -o -name configure.in -print` 105 | do 106 | dr=`dirname $coin` 107 | if test -f $dr/NO-AUTO-GEN; then 108 | echo skipping $dr -- flagged as no auto-gen 109 | else 110 | echo processing $dr 111 | ( cd $dr 112 | 113 | aclocalinclude="$ACLOCAL_FLAGS" 114 | 115 | if grep "^AM_GLIB_GNU_GETTEXT" configure.in >/dev/null; then 116 | echo "Creating $dr/aclocal.m4 ..." 117 | test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 118 | echo "Running glib-gettextize... Ignore non-fatal messages." 119 | echo "no" | glib-gettextize --force --copy 120 | echo "Making $dr/aclocal.m4 writable ..." 121 | test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 122 | fi 123 | if grep "^AC_PROG_INTLTOOL" configure.in >/dev/null; then 124 | echo "Running intltoolize..." 125 | intltoolize --copy --force --automake 126 | fi 127 | if grep "^AM_PROG_XML_I18N_TOOLS" configure.in >/dev/null; then 128 | echo "Running xml-i18n-toolize..." 129 | xml-i18n-toolize --copy --force --automake 130 | fi 131 | if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then 132 | if test -z "$NO_LIBTOOLIZE" ; then 133 | echo "Running libtoolize..." 134 | libtoolize --force --copy 135 | fi 136 | fi 137 | echo "Running aclocal $aclocalinclude ..." 138 | aclocal $aclocalinclude 139 | if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then 140 | echo "Running autoheader..." 141 | autoheader 142 | fi 143 | echo "Running automake --gnu $am_opt ..." 144 | automake --add-missing --gnu $am_opt 145 | echo "Running autoconf ..." 146 | autoconf 147 | ) 148 | fi 149 | done 150 | 151 | conf_flags="--enable-maintainer-mode" 152 | 153 | if test x$NOCONFIGURE = x; then 154 | echo Running $srcdir/configure $conf_flags "$@" ... 155 | $srcdir/configure $conf_flags "$@" \ 156 | && echo Now type \`make\' to compile. || exit 1 157 | else 158 | echo Skipping configure process. 159 | fi 160 | -------------------------------------------------------------------------------- /configure.in: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce a configure script. 2 | 3 | AC_INIT(configure.in) 4 | AM_INIT_AUTOMAKE(timemachine, 0.3.4) 5 | AM_CONFIG_HEADER(config.h) 6 | AM_MAINTAINER_MODE 7 | 8 | AC_ISC_POSIX 9 | AC_PROG_CC 10 | AM_PROG_CC_STDC 11 | AC_HEADER_STDC 12 | 13 | PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.0.0]) 14 | PKG_CHECK_MODULES(JACK, [jack >= 0.80.0]) 15 | PKG_CHECK_MODULES(LIBLO, liblo >= 0.24, LO_FOUND="yes", LO_FOUND="no") 16 | AC_CHECK_LIB([m],[pow]) 17 | 18 | if test "$LO_FOUND" = "yes"; then 19 | AC_DEFINE(HAVE_LIBLO, 1, [whether or not we are supporting OSC]) 20 | EXTRA_PROGRAMS="timemachine-control" 21 | AC_SUBST(LIBLO_CFLAGS) 22 | AC_SUBST(LIBLO_LIBS) 23 | fi 24 | 25 | ############## 26 | ### LASH ### 27 | ############## 28 | tm_enable_lash="yes" 29 | AC_ARG_ENABLE(lash, 30 | [ --disable-lash disable LASH support],[ 31 | case "$enableval" in 32 | "yes") 33 | ;; 34 | "no") 35 | tm_enable_lash="no" 36 | ;; 37 | *) 38 | AC_MSG_ERROR([must use --enable-lash(=yes/no) or --disable-lash]) 39 | ;; 40 | esac 41 | ]) 42 | 43 | if test "$tm_enable_lash" = "yes"; then 44 | PKG_CHECK_MODULES(LASH, lash-1.0 >= 0.4.0, LASH_FOUND="yes", LASH_FOUND="no") 45 | 46 | if test "$LASH_FOUND" = "yes"; then 47 | AC_DEFINE(HAVE_LASH, 1, [whether or not we are supporting lash]) 48 | LASH_VERSION=$( pkg-config --modversion lash-1.0 ) 49 | AC_DEFINE_UNQUOTED(LASH_VERSION, "$LASH_VERSION", [The version of lash we're compiling against]) 50 | AC_SUBST(LASH_CFLAGS) 51 | AC_SUBST(LASH_LIBS) 52 | fi 53 | else 54 | AC_MSG_WARN([LASH support is disabled]) 55 | LASH_FOUND="no" 56 | fi 57 | AM_CONDITIONAL(HAVE_LASH, test "$LASH_FOUND" = "yes") 58 | 59 | PKG_CHECK_MODULES(SNDFILE, [sndfile >= 1.0.0]) 60 | AC_CHECK_DECL( 61 | SF_FORMAT_W64, 62 | [AC_DEFINE(HAVE_W64, [], [Do we have the WAV64 format])], 63 | [AC_MSG_WARN() 64 | AC_MSG_WARN([Your version of libsndfile does not support the WAV64]) 65 | AC_MSG_WARN([format. This means that you will be unable to record]) 66 | AC_MSG_WARN([WAV files > 2GB]) 67 | AC_MSG_WARN()], 68 | [#include ] 69 | ) 70 | AC_CHECK_DECL( 71 | SF_FORMAT_FLAC, 72 | [AC_DEFINE(HAVE_FLAC, [], [Do we have the FLAC format])], 73 | [AC_MSG_WARN() 74 | AC_MSG_WARN([Your version of libsndfile does not support the FLAC]) 75 | AC_MSG_WARN([format.]) 76 | AC_MSG_WARN()], 77 | [#include ] 78 | ) 79 | 80 | AC_CHECK_LIB([curses], [tgetnum]) 81 | AC_CHECK_LIB([readline], [readline]) 82 | 83 | AC_CHECK_LIB([pthread], [pthread_self], , [AC_MSG_ERROR(Can't find libpthread)]) 84 | 85 | PACKAGE_CFLAGS="-g -Wall $GTK_CFLAGS $JACK_CFLAGS $LASH_CFLAGS $SNDFILE_CFLAGS $LIBLO_CFLAGS" 86 | PACKAGE_LIBS="-g $GTK_LIBS $JACK_LIBS $LASH_LIBS $SNDFILE_LIBS $LIBLO_LIBS" 87 | 88 | AC_SUBST(PACKAGE_CFLAGS) 89 | AC_SUBST(PACKAGE_LIBS) 90 | 91 | AC_OUTPUT([ 92 | Makefile 93 | src/Makefile 94 | pixmaps/Makefile 95 | ]) 96 | 97 | -------------------------------------------------------------------------------- /pixmaps/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = on.png off.png busy.png on-icon.png off-icon.png timemachine-icon.png 2 | 3 | pixmap_DATA = $(EXTRA_DIST) 4 | 5 | pixmapdir = $(datadir)/timemachine/pixmaps 6 | -------------------------------------------------------------------------------- /pixmaps/busy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/pixmaps/busy.png -------------------------------------------------------------------------------- /pixmaps/off-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/pixmaps/off-icon.png -------------------------------------------------------------------------------- /pixmaps/off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/pixmaps/off.png -------------------------------------------------------------------------------- /pixmaps/on-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/pixmaps/on-icon.png -------------------------------------------------------------------------------- /pixmaps/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/pixmaps/on.png -------------------------------------------------------------------------------- /pixmaps/timemachine-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swh/timemachine/6053ab0499859fce09117901ccb486c6d5de2d9f/pixmaps/timemachine-icon.png -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | timemachine 2 | timemachine-control 3 | .deps 4 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | INCLUDES = \ 4 | -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ 5 | -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ 6 | @PACKAGE_CFLAGS@ 7 | 8 | bin_PROGRAMS = timemachine 9 | 10 | timemachine_SOURCES = \ 11 | main.c main.h \ 12 | support.c support.h \ 13 | interface.c interface.h \ 14 | callbacks.c callbacks.h \ 15 | gtkmeter.c gtkmeter.h \ 16 | gtkmeterscale.c gtkmeterscale.h \ 17 | meters.c meters.h \ 18 | threads.c threads.h 19 | 20 | timemachine_LDADD = @PACKAGE_LIBS@ 21 | -------------------------------------------------------------------------------- /src/callbacks.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | # include 3 | #endif 4 | 5 | #include 6 | 7 | #include "main.h" 8 | #include "callbacks.h" 9 | #include "interface.h" 10 | #include "support.h" 11 | #include "gtkmeter.h" 12 | #include "gtkmeterscale.h" 13 | #include "threads.h" 14 | #ifdef HAVE_LIBLO 15 | #include 16 | #endif 17 | 18 | static int button_pressed = 0; 19 | 20 | void on_togglebutton1_clicked(GtkButton * button, gpointer user_data) 21 | { 22 | GtkWidget *img = lookup_widget(main_window, "toggle_image"); 23 | 24 | if (!GTK_WIDGET_IS_SENSITIVE(img)) { 25 | return; 26 | } 27 | 28 | button_pressed = !button_pressed; 29 | 30 | if (button_pressed) { 31 | recording_start(); 32 | gtk_image_set_from_pixbuf(GTK_IMAGE(img), img_on); 33 | gtk_window_set_icon(GTK_WINDOW(main_window), icon_on); 34 | } else { 35 | recording_stop(); 36 | gtk_widget_set_sensitive(img, FALSE); 37 | gtk_image_set_from_pixbuf(GTK_IMAGE(img), img_busy); 38 | gtk_window_set_icon(GTK_WINDOW(main_window), icon_off); 39 | } 40 | } 41 | 42 | gboolean on_window_delete_event(GtkWidget * widget, GdkEvent * event, 43 | gpointer user_data) 44 | { 45 | gtk_main_quit(); 46 | 47 | return FALSE; 48 | } 49 | 50 | #ifdef HAVE_LIBLO 51 | int osc_handler(const char *path, const char *types, lo_arg **argv, int argc, 52 | lo_message msg, void *user_data) 53 | { 54 | GtkWidget *img = lookup_widget(main_window, "toggle_image"); 55 | 56 | if (user_data) { 57 | recording_start(); 58 | gtk_image_set_from_pixbuf(GTK_IMAGE(img), img_on); 59 | gtk_window_set_icon(GTK_WINDOW(main_window), icon_on); 60 | } else { 61 | recording_stop(); 62 | gtk_widget_set_sensitive(img, FALSE); 63 | gtk_image_set_from_pixbuf(GTK_IMAGE(img), img_busy); 64 | gtk_window_set_icon(GTK_WINDOW(main_window), icon_off); 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | int osc_handler_nox(const char *path, const char *types, lo_arg **argv, 71 | int argc, lo_message msg, void *user_data) 72 | { 73 | if (user_data) { 74 | recording_start(); 75 | } else { 76 | recording_stop(); 77 | } 78 | 79 | return 0; 80 | } 81 | #endif 82 | -------------------------------------------------------------------------------- /src/callbacks.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | on_togglebutton1_clicked (GtkButton *button, 5 | gpointer user_data); 6 | 7 | gboolean 8 | on_window_delete_event (GtkWidget *widget, 9 | GdkEvent *event, 10 | gpointer user_data); 11 | -------------------------------------------------------------------------------- /src/gtkmeter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003 Steve Harris 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * $Id: gtkmeter.c,v 1.2 2005/01/22 17:17:39 swh Exp $ 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "gtkmeter.h" 23 | 24 | #define METER_DEFAULT_WIDTH 10 25 | #define METER_DEFAULT_LENGTH 100 26 | 27 | /* Forward declarations */ 28 | 29 | static void gtk_meter_class_init (GtkMeterClass *klass); 30 | static void gtk_meter_init (GtkMeter *meter); 31 | static void gtk_meter_destroy (GtkObject *object); 32 | static void gtk_meter_realize (GtkWidget *widget); 33 | static void gtk_meter_size_request (GtkWidget *widget, 34 | GtkRequisition *requisition); 35 | static void gtk_meter_size_allocate (GtkWidget *widget, 36 | GtkAllocation *allocation); 37 | static gint gtk_meter_expose (GtkWidget *widget, 38 | GdkEventExpose *event); 39 | static void gtk_meter_update (GtkMeter *meter); 40 | static void gtk_meter_adjustment_changed (GtkAdjustment *adjustment, 41 | gpointer data); 42 | static void gtk_meter_adjustment_value_changed (GtkAdjustment *adjustment, 43 | gpointer data); 44 | 45 | static float iec_scale(float db); 46 | 47 | /* Local data */ 48 | 49 | static GtkWidgetClass *parent_class = NULL; 50 | 51 | GtkType 52 | gtk_meter_get_type () 53 | { 54 | static GtkType meter_type = 0; 55 | 56 | if (!meter_type) 57 | { 58 | GtkTypeInfo meter_info = 59 | { 60 | "GtkMeter", 61 | sizeof (GtkMeter), 62 | sizeof (GtkMeterClass), 63 | (GtkClassInitFunc) gtk_meter_class_init, 64 | (GtkObjectInitFunc) gtk_meter_init, 65 | /*(GtkArgSetFunc)*/ NULL, 66 | /*(GtkArgGetFunc)*/ NULL, 67 | }; 68 | 69 | meter_type = gtk_type_unique (gtk_widget_get_type (), &meter_info); 70 | } 71 | 72 | return meter_type; 73 | } 74 | 75 | static void 76 | gtk_meter_class_init (GtkMeterClass *class) 77 | { 78 | GtkObjectClass *object_class; 79 | GtkWidgetClass *widget_class; 80 | 81 | object_class = (GtkObjectClass*) class; 82 | widget_class = (GtkWidgetClass*) class; 83 | 84 | parent_class = gtk_type_class (gtk_widget_get_type ()); 85 | 86 | object_class->destroy = gtk_meter_destroy; 87 | 88 | widget_class->realize = gtk_meter_realize; 89 | widget_class->expose_event = gtk_meter_expose; 90 | widget_class->size_request = gtk_meter_size_request; 91 | widget_class->size_allocate = gtk_meter_size_allocate; 92 | } 93 | 94 | static void 95 | gtk_meter_init (GtkMeter *meter) 96 | { 97 | meter->button = 0; 98 | meter->direction = GTK_METER_UP; 99 | meter->timer = 0; 100 | meter->amber_level = -6.0f; 101 | meter->amber_frac = 0.0f; 102 | meter->iec_lower = 0.0f; 103 | meter->iec_upper = 0.0f; 104 | meter->old_value = 0.0; 105 | meter->old_lower = 0.0; 106 | meter->old_upper = 0.0; 107 | meter->adjustment = NULL; 108 | } 109 | 110 | GtkWidget* 111 | gtk_meter_new (GtkAdjustment *adjustment, gint direction) 112 | { 113 | GtkMeter *meter; 114 | 115 | meter = gtk_type_new (gtk_meter_get_type ()); 116 | 117 | if (!adjustment) 118 | adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); 119 | 120 | gtk_meter_set_adjustment (meter, adjustment); 121 | meter->direction = direction; 122 | gtk_object_ref(GTK_OBJECT(meter)); 123 | 124 | return GTK_WIDGET (meter); 125 | } 126 | 127 | static void 128 | gtk_meter_destroy (GtkObject *object) 129 | { 130 | GtkMeter *meter; 131 | 132 | g_return_if_fail (object != NULL); 133 | g_return_if_fail (GTK_IS_METER (object)); 134 | 135 | meter = GTK_METER (object); 136 | 137 | if (meter->adjustment) 138 | gtk_object_unref (GTK_OBJECT (meter->adjustment)); 139 | 140 | if (GTK_OBJECT_CLASS (parent_class)->destroy) 141 | (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); 142 | } 143 | 144 | GtkAdjustment* 145 | gtk_meter_get_adjustment (GtkMeter *meter) 146 | { 147 | g_return_val_if_fail (meter != NULL, NULL); 148 | g_return_val_if_fail (GTK_IS_METER (meter), NULL); 149 | 150 | return meter->adjustment; 151 | } 152 | 153 | void 154 | gtk_meter_set_adjustment (GtkMeter *meter, 155 | GtkAdjustment *adjustment) 156 | { 157 | g_return_if_fail (meter != NULL); 158 | g_return_if_fail (GTK_IS_METER (meter)); 159 | 160 | if (meter->adjustment) { 161 | gtk_signal_disconnect_by_data (GTK_OBJECT (meter->adjustment), (gpointer) meter); 162 | gtk_object_unref (GTK_OBJECT (meter->adjustment)); 163 | } 164 | 165 | meter->adjustment = adjustment; 166 | gtk_object_ref (GTK_OBJECT (meter->adjustment)); 167 | 168 | gtk_signal_connect (GTK_OBJECT (adjustment), "changed", 169 | (GtkSignalFunc) gtk_meter_adjustment_changed, 170 | (gpointer) meter); 171 | gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", 172 | (GtkSignalFunc) gtk_meter_adjustment_value_changed, 173 | (gpointer) meter); 174 | 175 | meter->old_value = adjustment->value; 176 | meter->old_lower = adjustment->lower; 177 | meter->old_upper = adjustment->upper; 178 | meter->iec_lower = iec_scale(adjustment->lower); 179 | meter->iec_upper = iec_scale(adjustment->upper); 180 | meter->amber_frac = (iec_scale(meter->amber_level) - meter->iec_lower) / 181 | (meter->iec_upper - meter->iec_lower); 182 | 183 | gtk_meter_update (meter); 184 | } 185 | 186 | static void 187 | gtk_meter_realize (GtkWidget *widget) 188 | { 189 | GtkMeter *meter; 190 | GdkWindowAttr attributes; 191 | GdkColor green, amber, red, peak; 192 | gint attributes_mask; 193 | 194 | g_return_if_fail (widget != NULL); 195 | g_return_if_fail (GTK_IS_METER (widget)); 196 | 197 | GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); 198 | meter = GTK_METER (widget); 199 | 200 | attributes.x = widget->allocation.x; 201 | attributes.y = widget->allocation.y; 202 | attributes.width = widget->allocation.width; 203 | attributes.height = widget->allocation.height; 204 | attributes.wclass = GDK_INPUT_OUTPUT; 205 | attributes.window_type = GDK_WINDOW_CHILD; 206 | attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; 207 | attributes.visual = gtk_widget_get_visual (widget); 208 | attributes.colormap = gtk_widget_get_colormap (widget); 209 | 210 | attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; 211 | widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); 212 | 213 | widget->style = gtk_style_attach (widget->style, widget->window); 214 | 215 | gdk_window_set_user_data (widget->window, widget); 216 | 217 | gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); 218 | 219 | meter->green_gc = gdk_gc_new(widget->window); 220 | green.red = 0; 221 | green.green = 60000; 222 | green.blue = 0; 223 | gdk_colormap_alloc_color (attributes.colormap, &green, FALSE, TRUE); 224 | gdk_gc_set_foreground(meter->green_gc, &green); 225 | 226 | meter->amber_gc = gdk_gc_new(widget->window); 227 | amber.red = 50000; 228 | amber.green = 55000; 229 | amber.blue = 0; 230 | gdk_colormap_alloc_color (attributes.colormap, &amber, FALSE, TRUE); 231 | gdk_gc_set_foreground(meter->amber_gc, &amber); 232 | 233 | meter->red_gc = gdk_gc_new(widget->window); 234 | red.red = 60000; 235 | red.green = 0; 236 | red.blue = 0; 237 | gdk_colormap_alloc_color (attributes.colormap, &red, FALSE, TRUE); 238 | gdk_gc_set_foreground(meter->red_gc, &red); 239 | 240 | meter->peak_gc = gdk_gc_new(widget->window); 241 | peak.red = 60000; 242 | peak.green = 60000; 243 | peak.blue = 0; 244 | gdk_colormap_alloc_color (attributes.colormap, &peak, FALSE, TRUE); 245 | gdk_gc_set_foreground(meter->peak_gc, &peak); 246 | } 247 | 248 | static void 249 | gtk_meter_size_request (GtkWidget *widget, 250 | GtkRequisition *requisition) 251 | { 252 | GtkMeter *meter = GTK_METER(widget); 253 | 254 | if (meter->direction == GTK_METER_UP || meter->direction == GTK_METER_DOWN) { 255 | requisition->width = METER_DEFAULT_WIDTH; 256 | requisition->height = METER_DEFAULT_LENGTH; 257 | } else { 258 | requisition->width = METER_DEFAULT_LENGTH; 259 | requisition->height = METER_DEFAULT_WIDTH; 260 | } 261 | } 262 | 263 | static void 264 | gtk_meter_size_allocate (GtkWidget *widget, 265 | GtkAllocation *allocation) 266 | { 267 | GtkMeter *meter; 268 | 269 | g_return_if_fail (widget != NULL); 270 | g_return_if_fail (GTK_IS_METER (widget)); 271 | g_return_if_fail (allocation != NULL); 272 | 273 | widget->allocation = *allocation; 274 | meter = GTK_METER (widget); 275 | 276 | if (GTK_WIDGET_REALIZED (widget)) 277 | { 278 | 279 | gdk_window_move_resize (widget->window, 280 | allocation->x, allocation->y, 281 | allocation->width, allocation->height); 282 | 283 | } 284 | } 285 | 286 | static gint 287 | gtk_meter_expose (GtkWidget *widget, 288 | GdkEventExpose *event) 289 | { 290 | GtkMeter *meter; 291 | float val, frac, peak_frac; 292 | int g_h, a_h, r_h; 293 | int length = 0, width = 0; 294 | 295 | g_return_val_if_fail (widget != NULL, FALSE); 296 | g_return_val_if_fail (GTK_IS_METER (widget), FALSE); 297 | g_return_val_if_fail (event != NULL, FALSE); 298 | 299 | if (event->count > 0) 300 | return FALSE; 301 | 302 | meter = GTK_METER (widget); 303 | 304 | switch (meter->direction) { 305 | case GTK_METER_UP: 306 | case GTK_METER_DOWN: 307 | length = widget->allocation.height - 2; 308 | width = widget->allocation.width - 2; 309 | break; 310 | case GTK_METER_LEFT: 311 | case GTK_METER_RIGHT: 312 | length = widget->allocation.width - 2; 313 | width = widget->allocation.height - 2; 314 | break; 315 | } 316 | 317 | val = iec_scale(meter->adjustment->value); 318 | if (val > meter->peak) { 319 | if (val > meter->iec_upper) { 320 | meter->peak = meter->iec_upper; 321 | } else { 322 | meter->peak = val; 323 | } 324 | } 325 | 326 | frac = (val - meter->iec_lower) / (meter->iec_upper - meter->iec_lower); 327 | peak_frac = (meter->peak - meter->iec_lower) / (meter->iec_upper - 328 | meter->iec_lower); 329 | 330 | /* Draw the background */ 331 | gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL, 332 | GTK_SHADOW_IN, NULL, widget, "trough", 0, 0, 333 | widget->allocation.width, widget->allocation.height); 334 | 335 | 336 | if (frac < meter->amber_frac) { 337 | g_h = frac * length; 338 | a_h = g_h; 339 | r_h = g_h; 340 | } else if (val <= 100.0f) { 341 | g_h = meter->amber_frac * length; 342 | a_h = frac * length; 343 | r_h = a_h; 344 | } else { 345 | g_h = meter->amber_frac * length; 346 | a_h = length * (100.0f - meter->iec_lower) / (meter->iec_upper - 347 | meter->iec_lower); 348 | r_h = frac * length; 349 | } 350 | 351 | if (a_h > length) { 352 | a_h = length; 353 | } 354 | if (r_h > length) { 355 | r_h = length; 356 | } 357 | 358 | switch (meter->direction) { 359 | case GTK_METER_LEFT: 360 | gdk_draw_rectangle (widget->window, meter->amber_gc, TRUE, a_h + 361 | 1, 1, length - a_h, width); 362 | /* 363 | gdk_draw_rectangle (widget->window, meter->peak_gc, TRUE, length * 364 | (1.0 - peak_frac) + 1, 1, 1, width); 365 | */ 366 | break; 367 | 368 | case GTK_METER_RIGHT: 369 | gdk_draw_rectangle (widget->window, meter->green_gc, TRUE, 1, 1, 370 | g_h, width); 371 | if (a_h > g_h) { 372 | gdk_draw_rectangle (widget->window, meter->amber_gc, TRUE, 1+g_h, 1, 373 | a_h - g_h, width); 374 | } 375 | if (r_h > a_h) { 376 | gdk_draw_rectangle (widget->window, meter->red_gc, TRUE, 1+a_h, 1, 377 | r_h - a_h, width); 378 | } 379 | gdk_draw_rectangle (widget->window, meter->peak_gc, TRUE, length * 380 | peak_frac, 1, 1, width); 381 | break; 382 | 383 | case GTK_METER_UP: 384 | default: 385 | gdk_draw_rectangle (widget->window, meter->green_gc, TRUE, 1, length - 386 | g_h + 1, width, g_h); 387 | if (a_h > g_h) { 388 | gdk_draw_rectangle (widget->window, meter->amber_gc, TRUE, 1, length - 389 | a_h + 1, width, a_h - g_h); 390 | } 391 | if (r_h > a_h) { 392 | gdk_draw_rectangle (widget->window, meter->red_gc, TRUE, 1, length - 393 | r_h + 1, width, r_h - a_h); 394 | } 395 | if (peak_frac > 0) { 396 | gdk_draw_rectangle (widget->window, meter->peak_gc, TRUE, 1, length * 397 | (1.0f - peak_frac) + 1, width, 1); 398 | } 399 | break; 400 | } 401 | 402 | return FALSE; 403 | } 404 | 405 | static void 406 | gtk_meter_update (GtkMeter *meter) 407 | { 408 | gfloat new_value; 409 | 410 | g_return_if_fail (meter != NULL); 411 | g_return_if_fail (GTK_IS_METER (meter)); 412 | 413 | new_value = meter->adjustment->value; 414 | 415 | if (new_value < meter->adjustment->lower) 416 | new_value = meter->adjustment->lower; 417 | 418 | if (new_value > meter->adjustment->upper) 419 | new_value = meter->adjustment->upper; 420 | 421 | if (new_value != meter->adjustment->value) 422 | { 423 | meter->adjustment->value = new_value; 424 | gtk_signal_emit_by_name (GTK_OBJECT (meter->adjustment), "value_changed"); 425 | } 426 | 427 | gtk_widget_draw(GTK_WIDGET(meter), NULL); 428 | } 429 | 430 | static void 431 | gtk_meter_adjustment_changed (GtkAdjustment *adjustment, 432 | gpointer data) 433 | { 434 | GtkMeter *meter; 435 | 436 | g_return_if_fail (adjustment != NULL); 437 | g_return_if_fail (data != NULL); 438 | 439 | meter = GTK_METER (data); 440 | 441 | if ((meter->old_lower != adjustment->lower) || 442 | (meter->old_upper != adjustment->upper)) 443 | { 444 | meter->iec_lower = iec_scale(adjustment->lower); 445 | meter->iec_upper = iec_scale(adjustment->upper); 446 | 447 | gtk_meter_set_warn_point(meter, meter->amber_level); 448 | 449 | gtk_meter_update (meter); 450 | 451 | meter->old_value = adjustment->value; 452 | meter->old_lower = adjustment->lower; 453 | meter->old_upper = adjustment->upper; 454 | } else if (meter->old_value != adjustment->value) { 455 | gtk_meter_update (meter); 456 | 457 | meter->old_value = adjustment->value; 458 | } 459 | } 460 | 461 | static void 462 | gtk_meter_adjustment_value_changed (GtkAdjustment *adjustment, 463 | gpointer data) 464 | { 465 | GtkMeter *meter; 466 | 467 | g_return_if_fail (adjustment != NULL); 468 | g_return_if_fail (data != NULL); 469 | 470 | meter = GTK_METER (data); 471 | 472 | if (meter->old_value != adjustment->value) 473 | { 474 | gtk_meter_update (meter); 475 | 476 | meter->old_value = adjustment->value; 477 | } 478 | } 479 | 480 | static float iec_scale(float db) 481 | { 482 | float def = 0.0f; /* Meter deflection %age */ 483 | 484 | if (db < -70.0f) { 485 | def = 0.0f; 486 | } else if (db < -60.0f) { 487 | def = (db + 70.0f) * 0.25f; 488 | } else if (db < -50.0f) { 489 | def = (db + 60.0f) * 0.5f + 2.5f; 490 | } else if (db < -40.0f) { 491 | def = (db + 50.0f) * 0.75f + 7.5f; 492 | } else if (db < -30.0f) { 493 | def = (db + 40.0f) * 1.5f + 15.0f; 494 | } else if (db < -20.0f) { 495 | def = (db + 30.0f) * 2.0f + 30.0f; 496 | } else { 497 | def = (db + 20.0f) * 2.5f + 50.0f; 498 | } 499 | 500 | return def; 501 | } 502 | 503 | void gtk_meter_reset_peak(GtkMeter *meter) 504 | { 505 | meter->peak = 0.0f; 506 | } 507 | 508 | void gtk_meter_set_warn_point(GtkMeter *meter, gfloat pt) 509 | { 510 | meter->amber_level = pt; 511 | if (meter->direction == GTK_METER_LEFT || meter->direction == 512 | GTK_METER_DOWN) { 513 | meter->amber_frac = 1.0f - (iec_scale(meter->amber_level) - 514 | meter->iec_lower) / (meter->iec_upper - meter->iec_lower); 515 | } else { 516 | meter->amber_frac = (iec_scale(meter->amber_level) - meter->iec_lower) / 517 | (meter->iec_upper - meter->iec_lower); 518 | } 519 | 520 | gtk_widget_draw(GTK_WIDGET(meter), NULL); 521 | } 522 | -------------------------------------------------------------------------------- /src/gtkmeter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003 Steve Harris 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * $Id: gtkmeter.h,v 1.1.1.1 2004/02/17 23:33:50 swh Exp $ 15 | */ 16 | 17 | #ifndef __GTK_METER_H__ 18 | #define __GTK_METER_H__ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif /* __cplusplus */ 29 | 30 | 31 | #define GTK_METER(obj) GTK_CHECK_CAST (obj, gtk_meter_get_type (), GtkMeter) 32 | #define GTK_METER_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_meter_get_type (), GtkMeterClass) 33 | #define GTK_IS_METER(obj) GTK_CHECK_TYPE (obj, gtk_meter_get_type ()) 34 | 35 | #define GTK_METER_UP 0 36 | #define GTK_METER_DOWN 1 37 | #define GTK_METER_LEFT 2 38 | #define GTK_METER_RIGHT 3 39 | 40 | typedef struct _GtkMeter GtkMeter; 41 | typedef struct _GtkMeterClass GtkMeterClass; 42 | 43 | struct _GtkMeter 44 | { 45 | GtkWidget widget; 46 | 47 | /* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ 48 | guint direction : 2; 49 | 50 | /* Button currently pressed or 0 if none */ 51 | guint8 button; 52 | 53 | /* Amber dB and deflection points */ 54 | gfloat amber_level; 55 | gfloat amber_frac; 56 | 57 | /* Deflection limits */ 58 | gfloat iec_lower; 59 | gfloat iec_upper; 60 | 61 | /* Peak deflection */ 62 | gfloat peak; 63 | 64 | /* ID of update timer, or 0 if none */ 65 | guint32 timer; 66 | 67 | /* Old values from adjustment stored so we know when something changes */ 68 | gfloat old_value; 69 | gfloat old_lower; 70 | gfloat old_upper; 71 | 72 | GdkGC *green_gc; 73 | GdkGC *amber_gc; 74 | GdkGC *red_gc; 75 | GdkGC *peak_gc; 76 | 77 | /* The adjustment object that stores the data for this meter */ 78 | GtkAdjustment *adjustment; 79 | }; 80 | 81 | struct _GtkMeterClass 82 | { 83 | GtkWidgetClass parent_class; 84 | }; 85 | 86 | 87 | GtkWidget* gtk_meter_new (GtkAdjustment *adjustment, 88 | gint direction); 89 | 90 | GtkType gtk_meter_get_type (void); 91 | GtkAdjustment* gtk_meter_get_adjustment (GtkMeter *meter); 92 | 93 | void gtk_meter_set_adjustment (GtkMeter *meter, 94 | GtkAdjustment *adjustment); 95 | 96 | void gtk_meter_reset_peak (GtkMeter *meter); 97 | 98 | void gtk_meter_set_warn_point (GtkMeter *meter, 99 | gfloat pt); 100 | 101 | #ifdef __cplusplus 102 | } 103 | #endif /* __cplusplus */ 104 | 105 | 106 | #endif /* __GTK_METER */ 107 | -------------------------------------------------------------------------------- /src/gtkmeterscale.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003 Steve Harris 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * $Id: gtkmeterscale.c,v 1.1.1.1 2004/02/17 23:33:50 swh Exp $ 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "gtkmeterscale.h" 23 | 24 | #define METERSCALE_MAX_FONT_SIZE 8 25 | 26 | /* Forward declarations */ 27 | 28 | static void gtk_meterscale_class_init (GtkMeterScaleClass *klass); 29 | static void gtk_meterscale_init (GtkMeterScale *meterscale); 30 | static void gtk_meterscale_destroy (GtkObject *object); 31 | static void gtk_meterscale_realize (GtkWidget *widget); 32 | static void gtk_meterscale_size_request (GtkWidget *widget, 33 | GtkRequisition *requisition); 34 | static void gtk_meterscale_size_allocate (GtkWidget *widget, 35 | GtkAllocation *allocation); 36 | static gint gtk_meterscale_expose (GtkWidget *widget, 37 | GdkEventExpose *event); 38 | static float iec_scale(float db); 39 | static void meterscale_draw_notch_label(GtkMeterScale *meterscale, float db, 40 | int mark, PangoRectangle *last_label_rect); 41 | static void meterscale_draw_notch(GtkMeterScale *meterscale, float db, int 42 | mark); 43 | 44 | /* Local data */ 45 | 46 | static GtkWidgetClass *parent_class = NULL; 47 | 48 | GtkType 49 | gtk_meterscale_get_type () 50 | { 51 | static GtkType meterscale_type = 0; 52 | 53 | if (!meterscale_type) 54 | { 55 | GtkTypeInfo meterscale_info = 56 | { 57 | "GtkMeterScale", 58 | sizeof (GtkMeterScale), 59 | sizeof (GtkMeterScaleClass), 60 | (GtkClassInitFunc) gtk_meterscale_class_init, 61 | (GtkObjectInitFunc) gtk_meterscale_init, 62 | /*(GtkArgSetFunc)*/ NULL, 63 | /*(GtkArgGetFunc)*/ NULL, 64 | }; 65 | 66 | meterscale_type = gtk_type_unique (gtk_widget_get_type (), 67 | &meterscale_info); 68 | } 69 | 70 | return meterscale_type; 71 | } 72 | 73 | static void 74 | gtk_meterscale_class_init (GtkMeterScaleClass *class) 75 | { 76 | GtkObjectClass *object_class; 77 | GtkWidgetClass *widget_class; 78 | 79 | object_class = (GtkObjectClass*) class; 80 | widget_class = (GtkWidgetClass*) class; 81 | 82 | parent_class = gtk_type_class (gtk_widget_get_type ()); 83 | 84 | object_class->destroy = gtk_meterscale_destroy; 85 | 86 | widget_class->realize = gtk_meterscale_realize; 87 | widget_class->expose_event = gtk_meterscale_expose; 88 | widget_class->size_request = gtk_meterscale_size_request; 89 | widget_class->size_allocate = gtk_meterscale_size_allocate; 90 | } 91 | 92 | static void 93 | gtk_meterscale_init (GtkMeterScale *meterscale) 94 | { 95 | meterscale->direction = 0; 96 | meterscale->iec_lower = 0.0f; 97 | meterscale->iec_upper = 0.0f; 98 | meterscale->min_width = -1; 99 | meterscale->min_height = -1; 100 | } 101 | 102 | GtkWidget* 103 | gtk_meterscale_new (gint direction, float min, float max) 104 | { 105 | GtkMeterScale *meterscale; 106 | 107 | meterscale = gtk_type_new (gtk_meterscale_get_type ()); 108 | 109 | meterscale->direction = direction; 110 | meterscale->lower = min; 111 | meterscale->upper = max; 112 | meterscale->iec_lower = iec_scale(min); 113 | meterscale->iec_upper = iec_scale(max); 114 | 115 | gtk_object_ref(GTK_OBJECT(meterscale)); 116 | 117 | return GTK_WIDGET(meterscale); 118 | } 119 | 120 | static void 121 | gtk_meterscale_destroy (GtkObject *object) 122 | { 123 | GtkMeterScale *meterscale; 124 | 125 | g_return_if_fail (object != NULL); 126 | g_return_if_fail (GTK_IS_METERSCALE (object)); 127 | 128 | meterscale = GTK_METERSCALE (object); 129 | 130 | if (GTK_OBJECT_CLASS (parent_class)->destroy) 131 | (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); 132 | } 133 | 134 | static void 135 | gtk_meterscale_realize (GtkWidget *widget) 136 | { 137 | GtkMeterScale *meterscale; 138 | GdkWindowAttr attributes; 139 | gint attributes_mask; 140 | PangoContext *pc = gtk_widget_get_pango_context(widget); 141 | PangoLayout *pl; 142 | PangoFontDescription *pfd; 143 | PangoRectangle rect; 144 | 145 | g_return_if_fail (widget != NULL); 146 | g_return_if_fail (GTK_IS_METERSCALE (widget)); 147 | 148 | GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); 149 | meterscale = GTK_METERSCALE (widget); 150 | 151 | attributes.x = widget->allocation.x; 152 | attributes.y = widget->allocation.y; 153 | attributes.width = widget->allocation.width; 154 | attributes.height = widget->allocation.height; 155 | attributes.wclass = GDK_INPUT_OUTPUT; 156 | attributes.window_type = GDK_WINDOW_CHILD; 157 | attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; 158 | attributes.visual = gtk_widget_get_visual (widget); 159 | attributes.colormap = gtk_widget_get_colormap (widget); 160 | 161 | attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; 162 | widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); 163 | 164 | widget->style = gtk_style_attach (widget->style, widget->window); 165 | 166 | gdk_window_set_user_data (widget->window, widget); 167 | 168 | gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); 169 | 170 | pfd = pango_font_description_new(); 171 | pango_font_description_set_family(pfd, "sans"); 172 | pango_font_description_set_size(pfd, METERSCALE_MAX_FONT_SIZE * PANGO_SCALE); 173 | pango_context_set_font_description(pc, pfd); 174 | pl = pango_layout_new(pc); 175 | pango_layout_set_text(pl, "99", -1); 176 | pango_layout_get_pixel_extents(pl, &rect, NULL); 177 | 178 | if (meterscale->direction & GTK_METERSCALE_LEFT || 179 | meterscale->direction & GTK_METERSCALE_RIGHT) { 180 | meterscale->min_width = rect.width + METERSCALE_MAX_FONT_SIZE; 181 | } 182 | if (meterscale->direction & GTK_METERSCALE_TOP || 183 | meterscale->direction & GTK_METERSCALE_BOTTOM) { 184 | meterscale->min_height = rect.height + METERSCALE_MAX_FONT_SIZE + 1; 185 | } 186 | gtk_widget_set_usize(widget, meterscale->min_width, meterscale->min_height); 187 | } 188 | 189 | static void 190 | gtk_meterscale_size_request (GtkWidget *widget, 191 | GtkRequisition *requisition) 192 | { 193 | requisition->width = GTK_METERSCALE(widget)->min_width; 194 | requisition->height = GTK_METERSCALE(widget)->min_height; 195 | } 196 | 197 | static void 198 | gtk_meterscale_size_allocate (GtkWidget *widget, 199 | GtkAllocation *allocation) 200 | { 201 | GtkMeterScale *meterscale; 202 | 203 | g_return_if_fail (widget != NULL); 204 | g_return_if_fail (GTK_IS_METERSCALE (widget)); 205 | g_return_if_fail (allocation != NULL); 206 | 207 | widget->allocation = *allocation; 208 | meterscale = GTK_METERSCALE (widget); 209 | 210 | if (GTK_WIDGET_REALIZED (widget)) 211 | { 212 | 213 | gdk_window_move_resize (widget->window, 214 | allocation->x, allocation->y, 215 | allocation->width, allocation->height); 216 | 217 | } 218 | } 219 | 220 | static gint 221 | gtk_meterscale_expose (GtkWidget *widget, 222 | GdkEventExpose *event) 223 | { 224 | GtkMeterScale *meterscale; 225 | PangoRectangle lr; 226 | float val; 227 | 228 | g_return_val_if_fail (widget != NULL, FALSE); 229 | g_return_val_if_fail (GTK_IS_METERSCALE (widget), FALSE); 230 | g_return_val_if_fail (event != NULL, FALSE); 231 | 232 | if (event->count > 0) 233 | return FALSE; 234 | 235 | meterscale = GTK_METERSCALE (widget); 236 | 237 | /* Draw the background */ 238 | gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL, 239 | GTK_SHADOW_ETCHED_IN, NULL, widget, "trough", 0, 0, 240 | widget->allocation.width, widget->allocation.height); 241 | gdk_window_clear_area (widget->window, 1, 1, widget->allocation.width - 2, 242 | widget->allocation.height - 2); 243 | 244 | 245 | lr.x = 0; 246 | lr.y = 0; 247 | lr.width = 0; 248 | lr.height = 0; 249 | 250 | meterscale_draw_notch_label(meterscale, 0.0f, 3, &lr); 251 | 252 | for (val = 5.0f; val < meterscale->upper; val += 5.0f) { 253 | meterscale_draw_notch_label(meterscale, val, 2, &lr); 254 | } 255 | 256 | for (val = -5.0f; val > meterscale->lower; val -= 5.0f) { 257 | meterscale_draw_notch_label(meterscale, val, 2, &lr); 258 | } 259 | 260 | for (val = -10.0f; val < 10.0f; val += 1.0f) { 261 | meterscale_draw_notch(meterscale, val, 1); 262 | } 263 | 264 | return FALSE; 265 | } 266 | 267 | static void meterscale_draw_notch_label(GtkMeterScale *meterscale, float db, 268 | int mark, PangoRectangle *last_label_rect) 269 | { 270 | GtkWidget *widget = GTK_WIDGET(meterscale); 271 | int length, width, pos; 272 | int vertical = 0; 273 | 274 | if (meterscale->direction & GTK_METERSCALE_LEFT || 275 | meterscale->direction & GTK_METERSCALE_RIGHT) { 276 | length = widget->allocation.height - 2; 277 | width = widget->allocation.width - 2; 278 | pos = length - length * (iec_scale(db) - meterscale->iec_lower) / 279 | (meterscale->iec_upper - meterscale->iec_lower); 280 | vertical = 1; 281 | } else { 282 | length = widget->allocation.width - 2; 283 | width = widget->allocation.height - 2; 284 | pos = length * (iec_scale(db) - meterscale->iec_lower) / 285 | (meterscale->iec_upper - meterscale->iec_lower); 286 | } 287 | 288 | 289 | if (last_label_rect) { 290 | PangoContext *pc = gtk_widget_get_pango_context(widget); 291 | PangoLayout *pl; 292 | PangoFontDescription *pfd; 293 | PangoRectangle rect; 294 | char text[128]; 295 | int x, y, size; 296 | 297 | size = (6 + length / 150); 298 | if (size > METERSCALE_MAX_FONT_SIZE) { 299 | size = METERSCALE_MAX_FONT_SIZE; 300 | } 301 | pfd = pango_font_description_new(); 302 | pango_font_description_set_family(pfd, "sans"); 303 | pango_font_description_set_size(pfd, size * PANGO_SCALE); 304 | pango_context_set_font_description(pc, pfd); 305 | 306 | pl = pango_layout_new(pc); 307 | 308 | snprintf(text, 127, "%.0f", fabs(db)); 309 | pango_layout_set_text(pl, text, -1); 310 | pango_layout_get_pixel_extents(pl, NULL, &rect); 311 | 312 | if (vertical) { 313 | x = width/2 - rect.width/2 + 1; 314 | y = pos - rect.height/2; 315 | if (y < 1) { 316 | y = 1; 317 | } 318 | } else { 319 | x = pos - rect.width/2 + 1; 320 | y = width/2 - rect.height / 2 + 1; 321 | if (x < 1) { 322 | x = 1; 323 | } else if (x + rect.width > length) { 324 | x = length - rect.width + 1; 325 | } 326 | } 327 | 328 | if (vertical && last_label_rect->y < y + rect.height + 2 && last_label_rect->y + last_label_rect->height + 2 > y) { 329 | return; 330 | } 331 | if (!vertical && last_label_rect->x < x + rect.width + 2 && last_label_rect->x + last_label_rect->width + 2 > y) { 332 | return; 333 | } 334 | 335 | gdk_draw_layout(widget->window, widget->style->black_gc, x, y, pl); 336 | last_label_rect->width = rect.width; 337 | last_label_rect->height = rect.height; 338 | last_label_rect->x = x; 339 | last_label_rect->y = y; 340 | } 341 | 342 | meterscale_draw_notch(meterscale, db, mark); 343 | } 344 | 345 | static void meterscale_draw_notch(GtkMeterScale *meterscale, float db, 346 | int mark) 347 | { 348 | GtkWidget *widget = GTK_WIDGET(meterscale); 349 | int pos, length, width; 350 | 351 | if (meterscale->direction & GTK_METERSCALE_LEFT || 352 | meterscale->direction & GTK_METERSCALE_RIGHT) { 353 | length = widget->allocation.height - 2; 354 | width = widget->allocation.width - 2; 355 | pos = length - length * (iec_scale(db) - meterscale->iec_lower) / 356 | (meterscale->iec_upper - meterscale->iec_lower); 357 | } else { 358 | length = widget->allocation.width - 2; 359 | width = widget->allocation.height - 2; 360 | pos = length * (iec_scale(db) - meterscale->iec_lower) / 361 | (meterscale->iec_upper - meterscale->iec_lower); 362 | } 363 | 364 | if (meterscale->direction & GTK_METERSCALE_LEFT) { 365 | gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE, 366 | 0, pos, mark, 1); 367 | } 368 | if (meterscale->direction & GTK_METERSCALE_RIGHT) { 369 | gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE, 370 | width - mark + 1, pos, mark, 1); 371 | } 372 | if (meterscale->direction & GTK_METERSCALE_TOP) { 373 | gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE, 374 | pos+1, 1, 1, mark); 375 | } 376 | if (meterscale->direction & GTK_METERSCALE_BOTTOM) { 377 | gdk_draw_rectangle(widget->window, widget->style->black_gc, TRUE, 378 | pos+1, width -mark + 1, 1, mark); 379 | } 380 | } 381 | 382 | static float iec_scale(float db) 383 | { 384 | float def = 0.0f; /* MeterScale deflection %age */ 385 | 386 | if (db < -70.0f) { 387 | def = 0.0f; 388 | } else if (db < -60.0f) { 389 | def = (db + 70.0f) * 0.25f; 390 | } else if (db < -50.0f) { 391 | def = (db + 60.0f) * 0.5f + 5.0f; 392 | } else if (db < -40.0f) { 393 | def = (db + 50.0f) * 0.75f + 7.5; 394 | } else if (db < -30.0f) { 395 | def = (db + 40.0f) * 1.5f + 15.0f; 396 | } else if (db < -20.0f) { 397 | def = (db + 30.0f) * 2.0f + 30.0f; 398 | } else { 399 | def = (db + 20.0f) * 2.5f + 50.0f; 400 | } 401 | 402 | return def; 403 | } 404 | -------------------------------------------------------------------------------- /src/gtkmeterscale.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003 Steve Harris 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * $Id: gtkmeterscale.h,v 1.1.1.1 2004/02/17 23:33:50 swh Exp $ 15 | */ 16 | 17 | #ifndef __GTK_METERSCALE_H__ 18 | #define __GTK_METERSCALE_H__ 19 | 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif /* __cplusplus */ 29 | 30 | 31 | #define GTK_METERSCALE(obj) GTK_CHECK_CAST (obj, gtk_meterscale_get_type (), GtkMeterScale) 32 | #define GTK_METERSCALE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_meterscale_get_type (), GtkMeterScaleClass) 33 | #define GTK_IS_METERSCALE(obj) GTK_CHECK_TYPE (obj, gtk_meterscale_get_type ()) 34 | 35 | #define GTK_METERSCALE_LEFT 1 36 | #define GTK_METERSCALE_RIGHT 2 37 | #define GTK_METERSCALE_TOP 4 38 | #define GTK_METERSCALE_BOTTOM 8 39 | 40 | typedef struct _GtkMeterScale GtkMeterScale; 41 | typedef struct _GtkMeterScaleClass GtkMeterScaleClass; 42 | 43 | struct _GtkMeterScale 44 | { 45 | GtkWidget widget; 46 | 47 | /* the sides scales are marked on */ 48 | guint direction; 49 | 50 | /* Deflection limits */ 51 | gfloat lower; 52 | gfloat upper; 53 | gfloat iec_lower; 54 | gfloat iec_upper; 55 | 56 | int min_width; 57 | int min_height; 58 | }; 59 | 60 | struct _GtkMeterScaleClass 61 | { 62 | GtkWidgetClass parent_class; 63 | }; 64 | 65 | 66 | GtkWidget* gtk_meterscale_new (gint direction, 67 | gfloat min, 68 | gfloat max); 69 | 70 | GtkType gtk_meterscale_get_type (void); 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif /* __cplusplus */ 75 | 76 | 77 | #endif /* __GTK_METERSCALE */ 78 | -------------------------------------------------------------------------------- /src/interface.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | # include 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "main.h" 15 | #include "callbacks.h" 16 | #include "interface.h" 17 | #include "support.h" 18 | #include "gtkmeter.h" 19 | #include "gtkmeterscale.h" 20 | 21 | #define HOOKUP_OBJECT(component,widget,name) \ 22 | g_object_set_data_full (G_OBJECT (component), name, \ 23 | gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) 24 | 25 | #define HOOKUP_OBJECT_NO_REF(component,widget,name) \ 26 | g_object_set_data (G_OBJECT (component), name, widget) 27 | 28 | GtkWidget *make_meter(float lower, float upper) 29 | { 30 | GtkAdjustment *adjustment = (GtkAdjustment *) 31 | gtk_adjustment_new(-60.0f, lower, upper, 0.0, 0.0, 0.0); 32 | 33 | return gtk_meter_new(adjustment, GTK_METER_RIGHT); 34 | } 35 | 36 | GtkWidget *create_window(const char *title) 37 | { 38 | GtkWidget *window; 39 | GtkWidget *vbox1; 40 | GtkWidget *togglebutton1; 41 | GtkWidget *image1; 42 | GtkWidget *meter[MAX_PORTS]; 43 | GtkWidget *scale; 44 | int i; 45 | 46 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 47 | gtk_widget_set_name(window, "window"); 48 | gtk_window_set_title(GTK_WINDOW(window), title); 49 | 50 | vbox1 = gtk_vbox_new(FALSE, 0); 51 | gtk_widget_set_name(vbox1, "vbox1"); 52 | gtk_widget_show(vbox1); 53 | gtk_container_add(GTK_CONTAINER(window), vbox1); 54 | 55 | togglebutton1 = gtk_button_new(); 56 | gtk_widget_set_name(togglebutton1, "togglebutton1"); 57 | gtk_widget_show(togglebutton1); 58 | gtk_button_set_relief(GTK_BUTTON(togglebutton1), GTK_RELIEF_NONE); 59 | gtk_box_pack_start(GTK_BOX(vbox1), togglebutton1, FALSE, FALSE, 0); 60 | 61 | image1 = create_pixmap(window, "off.png"); 62 | gtk_widget_set_name(image1, "toggle_image"); 63 | gtk_widget_show(image1); 64 | gtk_container_add(GTK_CONTAINER(togglebutton1), image1); 65 | 66 | scale = gtk_meterscale_new(GTK_METERSCALE_BOTTOM, -60.0f, 6.0f); 67 | gtk_widget_set_name(scale, "scale_top"); 68 | gtk_widget_show(scale); 69 | gtk_box_pack_start(GTK_BOX(vbox1), scale, FALSE, TRUE, 0); 70 | gtk_widget_set_size_request(scale, -1, 10); 71 | GTK_WIDGET_UNSET_FLAGS(scale, GTK_CAN_FOCUS); 72 | GTK_WIDGET_UNSET_FLAGS(scale, GTK_CAN_DEFAULT); 73 | 74 | for (i=0; i 1) { 89 | scale = gtk_meterscale_new(GTK_METERSCALE_TOP, -60.0f, 6.0f); 90 | gtk_widget_set_name(scale, "scale_bottom"); 91 | gtk_widget_show(scale); 92 | gtk_box_pack_start(GTK_BOX(vbox1), scale, FALSE, TRUE, 0); 93 | gtk_widget_set_size_request(scale, -1, 10); 94 | GTK_WIDGET_UNSET_FLAGS(scale, GTK_CAN_FOCUS); 95 | GTK_WIDGET_UNSET_FLAGS(scale, GTK_CAN_DEFAULT); 96 | } 97 | 98 | g_signal_connect((gpointer) window, "delete_event", 99 | G_CALLBACK(on_window_delete_event), NULL); 100 | g_signal_connect((gpointer) togglebutton1, "clicked", 101 | G_CALLBACK(on_togglebutton1_clicked), NULL); 102 | 103 | /* Store pointers to all widgets, for use by lookup_widget(). */ 104 | HOOKUP_OBJECT_NO_REF(window, window, "window"); 105 | HOOKUP_OBJECT(window, vbox1, "vbox1"); 106 | HOOKUP_OBJECT(window, togglebutton1, "togglebutton1"); 107 | HOOKUP_OBJECT(window, image1, "toggle_image"); 108 | for (i=0; i 21 | #endif 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef HAVE_LASH 34 | #include 35 | 36 | lash_client_t *lash_client; 37 | #endif 38 | 39 | #ifdef HAVE_LIBREADLINE 40 | #include 41 | #include 42 | #endif 43 | 44 | #ifdef HAVE_LIBLO 45 | #include 46 | #endif 47 | 48 | #include "threads.h" 49 | #include "interface.h" 50 | #include "meters.h" 51 | #include "support.h" 52 | #include "main.h" 53 | 54 | #define DEBUG(lvl, txt...) \ 55 | if (verbosity >= lvl) fprintf(stderr, PACKAGE ": " txt) 56 | 57 | const static int verbosity = 0; 58 | 59 | gboolean idle_cb(gpointer data); 60 | void cleanup(); 61 | 62 | GtkWidget *main_window; 63 | 64 | int num_ports = DEFAULT_NUM_PORTS; 65 | unsigned int buf_length = DEFAULT_BUF_LENGTH; /* in seconds */ 66 | 67 | char *client_name = DEFAULT_CLIENT_NAME; 68 | char *prefix = DEFAULT_PREFIX; 69 | char *format_name = DEFAULT_FORMAT; 70 | int format_sf = 0; 71 | int safe_filename = 0; 72 | int auto_record = 0; 73 | float auto_begin_threshold = 0.0; 74 | float auto_end_threshold = 0.0; 75 | unsigned int auto_end_time = DEFAULT_AUTO_END_TIME; 76 | 77 | jack_port_t *ports[MAX_PORTS]; 78 | jack_client_t *client; 79 | 80 | GdkPixbuf *img_on, *img_off, *img_busy; 81 | GdkPixbuf *icon_on, *icon_off; 82 | 83 | #ifdef HAVE_LIBLO 84 | int osc_handler(const char *path, const char *types, lo_arg **argv, int argc, 85 | lo_message msg, void *user_data); 86 | int osc_handler_nox(const char *path, const char *types, lo_arg **argv, 87 | int argc, lo_message msg, void *user_data); 88 | char *osc_port = DEFAULT_OSC_PORT; 89 | #endif 90 | 91 | int main(int argc, char *argv[]) 92 | { 93 | unsigned int i; 94 | int opt; 95 | int help = 0; 96 | int console = 0; 97 | char port_name[32]; 98 | pthread_t dt; 99 | #ifdef HAVE_LASH 100 | lash_args_t *lash_args = lash_extract_args(&argc, &argv); 101 | lash_event_t *event; 102 | #endif 103 | 104 | auto_begin_threshold = db2lin(DEFAULT_AUTO_BEGIN_THRESHOLD); 105 | auto_end_threshold = db2lin(DEFAULT_AUTO_END_THRESHOLD); 106 | 107 | while ((opt = getopt(argc, argv, "hic:t:n:p:f:sab:e:T:o:")) != -1) { 108 | switch (opt) { 109 | case 'h': 110 | help = 1; 111 | break; 112 | case 'i': 113 | console = 1; 114 | break; 115 | case 'c': 116 | num_ports = atoi(optarg); 117 | DEBUG(1, "ports: %d\n", num_ports); 118 | break; 119 | case 't': 120 | buf_length = atoi(optarg); 121 | DEBUG(1, "buffer: %ds\n", buf_length); 122 | break; 123 | case 'n': 124 | client_name = optarg; 125 | DEBUG(1, "client name: %s\n", client_name); 126 | break; 127 | case 'p': 128 | prefix = optarg; 129 | DEBUG(1, "prefix: %s\n", prefix); 130 | break; 131 | case 'f': 132 | format_name = optarg; 133 | break; 134 | case 's': 135 | safe_filename = 1; 136 | break; 137 | case 'a': 138 | auto_record = 1; 139 | break; 140 | case 'b': 141 | auto_begin_threshold = db2lin(atof(optarg)); 142 | break; 143 | case 'e': 144 | auto_end_threshold = db2lin(atof(optarg)); 145 | break; 146 | case 'T': 147 | auto_end_time = atoi(optarg); 148 | break; 149 | case 'o': 150 | #ifdef HAVE_LIBLO 151 | osc_port = optarg; 152 | #endif 153 | break; 154 | default: 155 | num_ports = 0; 156 | break; 157 | } 158 | } 159 | 160 | if (optind != argc) { 161 | num_ports = argc - optind; 162 | } 163 | 164 | if (num_ports < 1 || num_ports > MAX_PORTS || help) { 165 | fprintf(stderr, "Usage %s: [-h] [-i] [-c channels] [-n jack-name]\n\t" 166 | "[-t buffer-length] [-p file prefix] [-f format]\n\t" 167 | "[-a] [-b begin-threshold] [-e end-threshold] [-T end-time]\n\t" 168 | "[port-name ...]\n\n", argv[0]); 169 | fprintf(stderr, "\t-h\tshow this help\n"); 170 | fprintf(stderr, "\t-i\tinteractive mode (console instead of X11) also enabled\n\t\tif DISPLAY is unset\n"); 171 | fprintf(stderr, "\t-c\tspecify number of recording channels\n"); 172 | fprintf(stderr, "\t-n\tspecify the JACK name timemachine will use\n"); 173 | fprintf(stderr, "\t-t\tspecify the pre-recording buffer length\n"); 174 | fprintf(stderr, "\t-p\tspecify the saved file prefix, may include path\n"); 175 | fprintf(stderr, "\t-s\tuse safer characters in filename (windows compatibility)\n"); 176 | fprintf(stderr, "\t-f\tspecify the saved file format\n"); 177 | fprintf(stderr, "\t-a\tenable automatic sound-triggered recording\n"); 178 | fprintf(stderr, "\t-b\tspecify threshold above which automatic recording will begin\n"); 179 | fprintf(stderr, "\t-e\tspecify threshold below which automatic recording will end\n"); 180 | fprintf(stderr, "\t-T\tspecify silence length before automatic recording ends\n"); 181 | #ifdef HAVE_LIBLO 182 | fprintf(stderr, "\t-o\tspecify the OSC port timemachine will listen on\n"); 183 | #endif 184 | fprintf(stderr, "\n"); 185 | fprintf(stderr, "\tchannels must be in the range 1-8, default %d\n", 186 | DEFAULT_NUM_PORTS); 187 | fprintf(stderr, "\tjack-name, default \"%s\"\n", DEFAULT_CLIENT_NAME); 188 | fprintf(stderr, "\tfile-prefix, default \"%s\"\n", DEFAULT_PREFIX); 189 | fprintf(stderr, "\tbuffer-length, default %d secs\n", DEFAULT_BUF_LENGTH); 190 | fprintf(stderr, "\tformat, default '%s', options: wav, w64, flac\n", DEFAULT_FORMAT); 191 | fprintf(stderr, "\tbegin-threshold, default %.1f dB\n", DEFAULT_AUTO_BEGIN_THRESHOLD); 192 | fprintf(stderr, "\tend-threshold, default %.1f dB\n", DEFAULT_AUTO_END_THRESHOLD); 193 | fprintf(stderr, "\tend-time, default %d secs\n", DEFAULT_AUTO_END_TIME); 194 | #ifdef HAVE_LIBLO 195 | fprintf(stderr, "\tosc-port, default %s\n", DEFAULT_OSC_PORT); 196 | #endif 197 | fprintf(stderr, "\n"); 198 | fprintf(stderr, "specifying port names to connect to on the command line overrides -c\n\n"); 199 | exit(1); 200 | } 201 | 202 | if (!strcasecmp(format_name, "wav")) { 203 | format_sf = SF_FORMAT_WAV | SF_FORMAT_FLOAT; 204 | } 205 | #ifdef HAVE_W64 206 | if (!strcasecmp(format_name, "w64")) { 207 | format_sf = SF_FORMAT_W64 | SF_FORMAT_FLOAT; 208 | } 209 | #endif 210 | #ifdef HAVE_FLAC 211 | if (!strcasecmp(format_name, "flac")) { 212 | format_sf = SF_FORMAT_FLAC | SF_FORMAT_PCM_24; 213 | } 214 | #endif 215 | 216 | if (format_sf == 0) { 217 | fprintf(stderr, "Unknown format '%s'\n", format_name); 218 | } 219 | 220 | /* Register with jack */ 221 | if ((client = jack_client_open(client_name, 0, NULL)) == 0) { 222 | DEBUG(0, "jack server not running?\n"); 223 | exit(1); 224 | } 225 | DEBUG(1, "registering as %s\n", client_name); 226 | 227 | process_init(buf_length); 228 | 229 | #ifdef HAVE_LASH 230 | lash_client = lash_init (lash_args, "TimeMachine", 231 | 0, /* would be LASH_Config_Data_Set etc. */ 232 | LASH_PROTOCOL (2,0)); 233 | if (!lash_client) { 234 | DEBUG(1, "could not initialise LASH\n"); 235 | } 236 | event = lash_event_new_with_type(LASH_Client_Name); 237 | lash_event_set_string(event, client_name); 238 | lash_send_event(lash_client, event); 239 | #endif 240 | 241 | jack_set_process_callback(client, process, 0); 242 | 243 | if (jack_activate(client)) { 244 | DEBUG(0, "cannot activate JACK client"); 245 | exit(1); 246 | } 247 | #ifdef HAVE_LASH 248 | lash_jack_client_name(lash_client, client_name); 249 | #endif 250 | 251 | /* Create the jack ports */ 252 | for (i = 0; i < num_ports; i++) { 253 | jack_port_t *port; 254 | 255 | snprintf(port_name, 31, "in_%d", i + 1); 256 | ports[i] = jack_port_register(client, port_name, 257 | JACK_DEFAULT_AUDIO_TYPE, 258 | JackPortIsInput, 0); 259 | if (optind != argc) { 260 | port = jack_port_by_name(client, argv[optind+i]); 261 | if (port == NULL) { 262 | fprintf(stderr, "Can't find port '%s'\n", port_name); 263 | continue; 264 | } 265 | if (jack_connect(client, argv[optind+i], jack_port_name(ports[i]))) { 266 | fprintf(stderr, "Cannot connect port '%s' to '%s'\n", 267 | argv[optind+i], jack_port_name(ports[i])); 268 | } 269 | } 270 | } 271 | 272 | /* Start the disk thread */ 273 | pthread_create(&dt, NULL, (void *)&writer_thread, NULL); 274 | 275 | #ifdef HAVE_LIBREADLINE 276 | if (console || !getenv("DISPLAY") || getenv("DISPLAY")[0] == '\0') { 277 | #ifdef HAVE_LIBLO 278 | lo_server_thread st = lo_server_thread_new(osc_port, NULL); 279 | if (st) { 280 | lo_server_thread_add_method(st, "/start", "", osc_handler_nox, (void *)1); 281 | lo_server_thread_add_method(st, "/stop", "", osc_handler_nox, (void *)0); 282 | lo_server_thread_start(st); 283 | printf("Listening for OSC requests on osc.udp://localhost:%s\n", 284 | osc_port); 285 | } 286 | #endif 287 | 288 | int done = 0; 289 | while (!done) { 290 | char *line = readline("TimeMachine> "); 291 | if (!line) { 292 | printf("EOF\n"); 293 | break; 294 | } 295 | if (line && *line) { 296 | add_history(line); 297 | if (strncmp(line, "q", 1) == 0) done = 1; 298 | else if (strncmp(line, "start", 3) == 0) recording_start(); 299 | else if (strncmp(line, "stop", 3) == 0) recording_stop(); 300 | else if (strncmp(line, "help", 3) == 0) { 301 | printf("Commands: start stop\n"); 302 | } else { 303 | printf("Unknown command\n"); 304 | } 305 | } 306 | free(line); 307 | } 308 | } else 309 | #endif 310 | { 311 | gtk_init(&argc, &argv); 312 | 313 | add_pixmap_directory(PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); 314 | add_pixmap_directory("pixmaps"); 315 | add_pixmap_directory("../pixmaps"); 316 | 317 | img_on = create_pixbuf("on.png"); 318 | img_off = create_pixbuf("off.png"); 319 | img_busy = create_pixbuf("busy.png"); 320 | icon_on = create_pixbuf("on-icon.png"); 321 | icon_off = create_pixbuf("off-icon.png"); 322 | 323 | main_window = create_window(client_name); 324 | gtk_window_set_icon(GTK_WINDOW(main_window), icon_off); 325 | gtk_widget_show(main_window); 326 | 327 | bind_meters(); 328 | g_timeout_add(100, meter_tick, NULL); 329 | 330 | #ifdef HAVE_LIBLO 331 | lo_server_thread st = lo_server_thread_new(osc_port, NULL); 332 | if (st) { 333 | lo_server_thread_add_method(st, "/start", "", osc_handler, (void *)1); 334 | lo_server_thread_add_method(st, "/stop", "", osc_handler, (void *)0); 335 | lo_server_thread_start(st); 336 | printf("Listening for OSC requests on osc.udp://localhost:%s\n", 337 | osc_port); 338 | } 339 | #endif 340 | 341 | #ifdef HAVE_LASH 342 | gtk_idle_add(idle_cb, lash_client); 343 | #endif 344 | 345 | gtk_main(); 346 | } 347 | 348 | cleanup(); 349 | 350 | /* We can't ever get here, but it keeps gcc quiet */ 351 | return 0; 352 | } 353 | 354 | void cleanup() 355 | { 356 | /* Leave the jack graph */ 357 | jack_client_close(client); 358 | 359 | recording_quit(); 360 | 361 | while(!recording_done) { 362 | usleep(1000); 363 | } 364 | 365 | DEBUG(0, "exiting\n"); 366 | fflush(stderr); 367 | 368 | /* And were done */ 369 | exit(0); 370 | } 371 | 372 | #ifdef HAVE_LASH 373 | gboolean idle_cb(gpointer data) 374 | { 375 | lash_client_t *lash_client = (lash_client_t *)data; 376 | lash_event_t *event; 377 | lash_config_t *config; 378 | 379 | while ((event = lash_get_event(lash_client))) { 380 | if (lash_event_get_type(event) == LASH_Save_Data_Set) { 381 | /* we can ignore this as timemachine has no state thats not on the 382 | * command line */ 383 | } else if (lash_event_get_type(event) == LASH_Quit) { 384 | cleanup(); 385 | } else { 386 | DEBUG(0, "unhandled LASH event: type %d, '%s''\n", 387 | lash_event_get_type(event), 388 | lash_event_get_string(event)); 389 | } 390 | } 391 | 392 | while ((config = lash_get_config(lash_client))) { 393 | DEBUG(0, "got unexpected LASH config: %s\n", 394 | lash_config_get_key(config)); 395 | } 396 | 397 | usleep(10000); 398 | 399 | return TRUE; 400 | } 401 | #endif 402 | 403 | /* vi:set ts=8 sts=4 sw=4: */ 404 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | 9 | #define MAX_PORTS 8 10 | #define MAX_TIME 3600 11 | 12 | #define DEFAULT_BUF_LENGTH 10 /* in seconds */ 13 | #define DEFAULT_NUM_PORTS 2 14 | #define DEFAULT_CLIENT_NAME "TimeMachine" 15 | #define DEFAULT_PREFIX "tm-" 16 | 17 | #ifdef HAVE_W64 18 | #define DEFAULT_FORMAT "w64" 19 | #else 20 | #define DEFAULT_FORMAT "wav" 21 | #endif 22 | 23 | #define DEFAULT_OSC_PORT "7133" 24 | 25 | #define DEFAULT_AUTO_BEGIN_THRESHOLD -35.0 26 | #define DEFAULT_AUTO_END_THRESHOLD DEFAULT_AUTO_BEGIN_THRESHOLD 27 | #define DEFAULT_AUTO_END_TIME DEFAULT_BUF_LENGTH 28 | 29 | extern GtkWidget *main_window; 30 | 31 | extern GdkPixbuf *img_on, *img_off, *img_busy; 32 | extern GdkPixbuf *icon_on, *icon_off; 33 | 34 | extern int num_ports; 35 | extern char *prefix; 36 | extern char *format_name; 37 | extern int format_sf; 38 | extern int safe_filename; 39 | extern int auto_record; 40 | extern float auto_begin_threshold; 41 | extern float auto_end_threshold; 42 | extern unsigned int auto_end_time; 43 | extern jack_client_t *client; 44 | extern jack_port_t *ports[MAX_PORTS]; 45 | extern char *osc_port; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/meters.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2004 Steve Harris 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * $Id: meters.c,v 1.1.1.1 2004/02/17 23:33:50 swh Exp $ 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "main.h" 24 | #include "gtkmeter.h" 25 | #include "threads.h" 26 | #include "support.h" 27 | #include "meters.h" 28 | 29 | static GtkAdjustment *meter_adj[MAX_PORTS]; 30 | 31 | void bind_meters() 32 | { 33 | unsigned int i; 34 | 35 | for (i=0; i 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "support.h" 18 | 19 | GtkWidget* 20 | lookup_widget (GtkWidget *widget, 21 | const gchar *widget_name) 22 | { 23 | GtkWidget *parent, *found_widget; 24 | 25 | for (;;) 26 | { 27 | if (GTK_IS_MENU (widget)) 28 | parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); 29 | else 30 | parent = widget->parent; 31 | if (!parent) 32 | parent = g_object_get_data (G_OBJECT (widget), "GladeParentKey"); 33 | if (parent == NULL) 34 | break; 35 | widget = parent; 36 | } 37 | 38 | found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), 39 | widget_name); 40 | if (!found_widget) 41 | g_warning ("Widget not found: %s", widget_name); 42 | return found_widget; 43 | } 44 | 45 | static GList *pixmaps_directories = NULL; 46 | 47 | /* Use this function to set the directory containing installed pixmaps. */ 48 | void 49 | add_pixmap_directory (const gchar *directory) 50 | { 51 | pixmaps_directories = g_list_prepend (pixmaps_directories, 52 | g_strdup (directory)); 53 | } 54 | 55 | /* This is an internally used function to find pixmap files. */ 56 | static gchar* 57 | find_pixmap_file (const gchar *filename) 58 | { 59 | GList *elem; 60 | 61 | /* We step through each of the pixmaps directory to find it. */ 62 | elem = pixmaps_directories; 63 | while (elem) 64 | { 65 | gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data, 66 | G_DIR_SEPARATOR_S, filename); 67 | if (g_file_test (pathname, G_FILE_TEST_EXISTS)) 68 | return pathname; 69 | g_free (pathname); 70 | elem = elem->next; 71 | } 72 | return NULL; 73 | } 74 | 75 | /* This is an internally used function to create pixmaps. */ 76 | GtkWidget* 77 | create_pixmap (GtkWidget *widget, 78 | const gchar *filename) 79 | { 80 | gchar *pathname = NULL; 81 | GtkWidget *pixmap; 82 | 83 | if (!filename || !filename[0]) 84 | return gtk_image_new (); 85 | 86 | pathname = find_pixmap_file (filename); 87 | 88 | if (!pathname) 89 | { 90 | g_warning ("Couldn't find pixmap file: %s", filename); 91 | return gtk_image_new (); 92 | } 93 | 94 | pixmap = gtk_image_new_from_file (pathname); 95 | g_free (pathname); 96 | return pixmap; 97 | } 98 | 99 | /* This is an internally used function to create pixmaps. */ 100 | GdkPixbuf* 101 | create_pixbuf (const gchar *filename) 102 | { 103 | gchar *pathname = NULL; 104 | GdkPixbuf *pixbuf; 105 | GError *error = NULL; 106 | 107 | if (!filename || !filename[0]) 108 | return NULL; 109 | 110 | pathname = find_pixmap_file (filename); 111 | 112 | if (!pathname) 113 | { 114 | g_warning ("Couldn't find pixmap file: %s", filename); 115 | return NULL; 116 | } 117 | 118 | pixbuf = gdk_pixbuf_new_from_file (pathname, &error); 119 | if (!pixbuf) 120 | { 121 | fprintf (stderr, "Failed to load pixbuf file: %s: %s\n", 122 | pathname, error->message); 123 | g_error_free (error); 124 | } 125 | g_free (pathname); 126 | return pixbuf; 127 | } 128 | 129 | /* This is used to set ATK action descriptions. */ 130 | void 131 | glade_set_atk_action_description (AtkAction *action, 132 | const gchar *action_name, 133 | const gchar *description) 134 | { 135 | gint n_actions, i; 136 | 137 | n_actions = atk_action_get_n_actions (action); 138 | for (i = 0; i < n_actions; i++) 139 | { 140 | if (!strcmp (atk_action_get_name (action, i), action_name)) 141 | atk_action_set_description (action, i, description); 142 | } 143 | } 144 | 145 | -------------------------------------------------------------------------------- /src/support.h: -------------------------------------------------------------------------------- 1 | /* 2 | * DO NOT EDIT THIS FILE - it is generated by Glade. 3 | */ 4 | 5 | #ifdef HAVE_CONFIG_H 6 | # include 7 | #endif 8 | 9 | #include 10 | 11 | /* 12 | * Public Functions. 13 | */ 14 | 15 | /* 16 | * This function returns a widget in a component created by Glade. 17 | * Call it with the toplevel widget in the component (i.e. a window/dialog), 18 | * or alternatively any widget in the component, and the name of the widget 19 | * you want returned. 20 | */ 21 | GtkWidget* lookup_widget (GtkWidget *widget, 22 | const gchar *widget_name); 23 | 24 | 25 | /* Use this function to set the directory containing installed pixmaps. */ 26 | void add_pixmap_directory (const gchar *directory); 27 | 28 | 29 | /* 30 | * Private Functions. 31 | */ 32 | 33 | /* This is used to create the pixmaps used in the interface. */ 34 | GtkWidget* create_pixmap (GtkWidget *widget, 35 | const gchar *filename); 36 | 37 | /* This is used to create the pixbufs used in the interface. */ 38 | GdkPixbuf* create_pixbuf (const gchar *filename); 39 | 40 | /* This is used to set ATK action descriptions. */ 41 | void glade_set_atk_action_description (AtkAction *action, 42 | const gchar *action_name, 43 | const gchar *description); 44 | 45 | -------------------------------------------------------------------------------- /src/threads.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2004 Steve Harris 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * $Id: threads.c,v 1.5 2005/01/22 17:17:39 swh Exp $ 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "config.h" 28 | #include "support.h" 29 | #include "main.h" 30 | #include "threads.h" 31 | #include "meters.h" 32 | 33 | #define BUF_SIZE 4096 34 | 35 | float *pre_buffer[MAX_PORTS]; 36 | float *disk_buffer[MAX_PORTS]; 37 | 38 | static int recording = 0; /* recording yes/no */ 39 | static int user_recording = 0; /* recording initiated by user yes/no */ 40 | static int quiting = 0; /* quit pending yes/no */ 41 | volatile int recording_done = 0; /* recording completed after quit */ 42 | volatile int need_ui_sync = 0; /* need to update record button */ 43 | 44 | static unsigned int pre_time = 0; 45 | static unsigned int pre_size = 0; 46 | static unsigned int pre_pos = 0; 47 | static unsigned int disk_read_pos = 0; 48 | static unsigned int disk_write_pos = 0; 49 | 50 | /* Peak data for meters */ 51 | static volatile float peak[MAX_PORTS]; 52 | 53 | static unsigned int silent_count = 0; 54 | 55 | int process(jack_nframes_t nframes, void *arg) 56 | { 57 | unsigned int i, port, pos = 0; 58 | const unsigned int rec = recording; 59 | const jack_nframes_t sample_rate = jack_get_sample_rate(client); 60 | 61 | for (port = 0; port < num_ports; port++) { 62 | jack_default_audio_sample_t *in; 63 | 64 | /* just incase the port isn't registered yet */ 65 | if (ports[port] == NULL) { 66 | break; 67 | } 68 | 69 | in = (jack_default_audio_sample_t *) 70 | jack_port_get_buffer(ports[port], nframes); 71 | 72 | if (!in) { 73 | fprintf(stderr, "bad buffer!\n"); 74 | break; 75 | } 76 | 77 | if (auto_record) { 78 | if (rec && !user_recording) { 79 | for (i = 0; i < nframes; i++) { 80 | if (fabsf(in[i]) <= auto_end_threshold) { 81 | silent_count++; 82 | } else { 83 | silent_count = 0; 84 | } 85 | } 86 | if (silent_count > (auto_end_time * sample_rate * num_ports)) { 87 | recording = 0; 88 | } 89 | } else { 90 | for (i = 0; i < nframes; i++) { 91 | if (fabsf(in[i]) > auto_begin_threshold) { 92 | recording = 1; 93 | silent_count = 0; 94 | break; 95 | } 96 | } 97 | } 98 | } 99 | 100 | for (i = 0; i < nframes; i++) { 101 | if (fabsf(in[i]) > peak[port]) { 102 | peak[port] = fabsf(in[i]); 103 | } 104 | } 105 | 106 | if (rec) { 107 | pos = disk_write_pos; 108 | for (i = 0; i < nframes; i++) { 109 | disk_buffer[port][pos] = in[i]; 110 | pos = (pos + 1) & (DISK_SIZE - 1); 111 | } 112 | } else { 113 | pos = pre_pos; 114 | for (i = 0; i < nframes; i++) { 115 | pre_buffer[port][pos++] = in[i]; 116 | if (pos > pre_size) { 117 | pos = 0; 118 | } 119 | } 120 | } 121 | } 122 | if (rec) { 123 | disk_write_pos = (disk_write_pos + nframes) & (DISK_SIZE - 1); 124 | } else { 125 | pre_pos = pos; 126 | } 127 | 128 | return 0; 129 | } 130 | 131 | int writer_thread(void *d) 132 | { 133 | unsigned int i, j, k, opos; 134 | char *filename; 135 | SNDFILE *out; 136 | SF_INFO info; 137 | float buf[BUF_SIZE * MAX_PORTS]; 138 | time_t t; 139 | struct tm *parts; 140 | 141 | again: 142 | while (!recording && !quiting) { 143 | usleep(100); 144 | 145 | } 146 | if (quiting) { 147 | recording_done = 1; 148 | 149 | return 0; 150 | } 151 | 152 | /* Find the ISO 8601 date string for the time of the start of the 153 | * buffer */ 154 | time(&t); 155 | t -= pre_time; 156 | parts = localtime(&t); 157 | if (safe_filename) { 158 | filename = g_strdup_printf("%s%04d-%02d-%02dT%02d-%02d-%02d.%s", 159 | prefix, 160 | parts->tm_year + 1900, parts->tm_mon + 1, parts->tm_mday, 161 | parts->tm_hour, parts->tm_min, parts->tm_sec, format_name); 162 | } else { 163 | filename = g_strdup_printf("%s%04d-%02d-%02dT%02d:%02d:%02d.%s", 164 | prefix, 165 | parts->tm_year + 1900, parts->tm_mon + 1, parts->tm_mday, 166 | parts->tm_hour, parts->tm_min, parts->tm_sec, format_name); 167 | } 168 | 169 | /* Open the output file */ 170 | info.samplerate = jack_get_sample_rate(client); 171 | info.channels = num_ports; 172 | 173 | info.format = format_sf; 174 | if (!sf_format_check(&info)) { 175 | fprintf(stderr, PACKAGE ": output file format error\n"); 176 | } 177 | 178 | out = sf_open(filename, SFM_WRITE, &info); 179 | if (!out) { 180 | perror("cannot open file for writing"); 181 | goto again; 182 | } 183 | 184 | printf("opened '%s'\n", filename); 185 | printf("writing buffer...\n"); 186 | free(filename); 187 | 188 | /* Dump the pre buffer to disk as fast was we can */ 189 | i = 0; 190 | while (i < pre_size) { 191 | for (j = 0; j < BUF_SIZE && i < pre_size; i++, j++) { 192 | for (k = 0; k < num_ports; k++) { 193 | buf[j * num_ports + k] = 194 | pre_buffer[k][(i + pre_pos) % pre_size]; 195 | } 196 | } 197 | sf_writef_float(out, buf, j); 198 | } 199 | 200 | /* Clear the pre buffer so we dont get stale data in it */ 201 | for (i = 0; i < num_ports; i++) { 202 | memset(pre_buffer[i], 0, pre_size * sizeof(float)); 203 | } 204 | 205 | /* This tells the UI that were ready to go again, it will reset it */ 206 | need_ui_sync = 1; 207 | 208 | if (recording) printf("writing realtime data...\n"); 209 | 210 | /* Start writing the RT ringbuffer to disk */ 211 | opos = 0; 212 | while (recording) { 213 | for (i = disk_read_pos; i != disk_write_pos && opos < BUF_SIZE; 214 | i = (i + 1) & (DISK_SIZE - 1), opos++) { 215 | for (j = 0; j < num_ports; j++) { 216 | buf[opos * num_ports + j] = disk_buffer[j][i]; 217 | } 218 | } 219 | sf_writef_float(out, buf, opos); 220 | disk_read_pos = i; 221 | opos = 0; 222 | usleep(10); 223 | } 224 | sf_close(out); 225 | 226 | need_ui_sync = 1; 227 | 228 | printf("done writing...\n"); 229 | 230 | /* Just make sure everythings reset */ 231 | disk_read_pos = disk_write_pos; 232 | 233 | /* Ugh, I'm sorry */ 234 | if (!quiting) goto again; 235 | 236 | recording_done = 1; 237 | 238 | return 0; 239 | } 240 | 241 | void process_init(unsigned int time) 242 | { 243 | unsigned int port; 244 | 245 | if (time < 1) { 246 | fprintf(stderr, "timemachine: buffer time must be 1 second or " 247 | "greater\n"); 248 | exit(1); 249 | } 250 | if (time > MAX_TIME) { 251 | fprintf(stderr, "timemachine: buffer time must be %d seconds or " 252 | "less\nthis is for your own good, it really will not" 253 | "work well with buffers that size\n", MAX_TIME); 254 | exit(1); 255 | } 256 | 257 | pre_size = time * jack_get_sample_rate(client); 258 | pre_time = time; 259 | 260 | for (port = 0; port < num_ports; port++) { 261 | pre_buffer[port] = calloc(pre_size, sizeof(float)); 262 | disk_buffer[port] = calloc(DISK_SIZE, sizeof(float)); 263 | } 264 | /* just make sure that if we try these ports we will SEGV */ 265 | for (; port < MAX_PORTS; port++) { 266 | pre_buffer[port] = NULL; 267 | disk_buffer[port] = NULL; 268 | } 269 | } 270 | 271 | void recording_start() 272 | { 273 | recording = 1; 274 | user_recording = 1; 275 | } 276 | 277 | void recording_stop() 278 | { 279 | recording = 0; 280 | user_recording = 0; 281 | } 282 | 283 | void recording_quit() 284 | { 285 | quiting = 1; 286 | recording_stop(); 287 | } 288 | 289 | gboolean meter_tick(gpointer user_data) 290 | { 291 | float data[MAX_PORTS]; 292 | unsigned int i; 293 | 294 | if (need_ui_sync) { 295 | GtkWidget *img = lookup_widget(main_window, "toggle_image"); 296 | 297 | if (recording) { 298 | gtk_image_set_from_pixbuf(GTK_IMAGE(img), img_on); 299 | gtk_window_set_icon(GTK_WINDOW(main_window), icon_on); 300 | } else { 301 | gtk_image_set_from_pixbuf(GTK_IMAGE(img), img_off); 302 | gtk_window_set_icon(GTK_WINDOW(main_window), icon_off); 303 | } 304 | gtk_widget_set_sensitive(img, TRUE); 305 | 306 | need_ui_sync = 0; 307 | } 308 | 309 | for (i=0; i 5 | #include 6 | 7 | /* size of disk buffer, must be power of two */ 8 | #define DISK_SIZE 131072 9 | 10 | int process(jack_nframes_t nframes, void *arg); 11 | 12 | void process_init(unsigned int time); 13 | 14 | int writer_thread(void *d); 15 | 16 | void recording_start(); 17 | void recording_stop(); 18 | void recording_quit(); 19 | 20 | gboolean meter_tick(gpointer data); 21 | 22 | extern volatile int recording_done; 23 | 24 | #endif 25 | --------------------------------------------------------------------------------