mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-27 09:38:39 +02:00
------------------------------------------------------------------------ r288 | rbd | 2018-09-25 13:47:35 -0500 (Tue, 25 Sep 2018) | 2 lines removed some redundant files that moved to nyquist extensions ------------------------------------------------------------------------ r287 | rbd | 2018-09-25 13:02:34 -0500 (Tue, 25 Sep 2018) | 2 lines Cleaning up: many things in demos have moved to Nyquist extensions ------------------------------------------------------------------------ r286 | rbd | 2018-09-11 08:21:16 -0500 (Tue, 11 Sep 2018) | 2 lines Releasing 3.15 on Mac, finished extension manager update to allow a custom extension list file set in Preferences. Allow text copy of checksum when checksums do not match (so IDE can calculate the checksum for new extensions), describe this in manual. ------------------------------------------------------------------------ r285 | rbd | 2018-09-05 08:00:03 -0500 (Wed, 05 Sep 2018) | 2 lines Changes for 3.15 on Mac and hopefully good for Windows too. ------------------------------------------------------------------------ r284 | rbd | 2018-09-01 21:21:47 -0500 (Sat, 01 Sep 2018) | 2 lines Final change? for v3.14 on Mac ------------------------------------------------------------------------ r283 | rbd | 2018-09-01 21:14:11 -0500 (Sat, 01 Sep 2018) | 2 lines Took out unicode char from fm-voices-chowning, added README to extensions directory ------------------------------------------------------------------------ r282 | rbd | 2018-09-01 21:12:47 -0500 (Sat, 01 Sep 2018) | 1 line fixed some instrument defns for sound browser ------------------------------------------------------------------------ r281 | rbd | 2018-09-01 19:56:55 -0500 (Sat, 01 Sep 2018) | 1 line Small changes for release on Windows: v3.14 ------------------------------------------------------------------------ r280 | rbd | 2018-09-01 19:56:07 -0500 (Sat, 01 Sep 2018) | 2 lines path problems in some extensions and minor changes for mac release ------------------------------------------------------------------------ r279 | rbd | 2018-09-01 15:03:26 -0500 (Sat, 01 Sep 2018) | 2 lines change doc/README.txt to doc/readme-mac.txt in install dmg ------------------------------------------------------------------------ r278 | rbd | 2018-09-01 14:58:10 -0500 (Sat, 01 Sep 2018) | 2 lines minor fix for mac release ------------------------------------------------------------------------ r277 | rbd | 2018-09-01 14:40:10 -0500 (Sat, 01 Sep 2018) | 2 lines Retry release on Mac, minor changes. ------------------------------------------------------------------------ r276 | rbd | 2018-08-31 23:44:45 -0500 (Fri, 31 Aug 2018) | 3 lines Updated documentation; building 3.14 for mac release ------------------------------------------------------------------------ r275 | rbd | 2018-08-31 21:20:41 -0500 (Fri, 31 Aug 2018) | 1 line Fixed extension manager to update installed flags correctly (in Swing thread). Tested browser on Windows. ------------------------------------------------------------------------ r274 | rbd | 2018-08-31 14:35:22 -0500 (Fri, 31 Aug 2018) | 1 line Forgot to add new file. ------------------------------------------------------------------------ r273 | rbd | 2018-08-31 14:33:02 -0500 (Fri, 31 Aug 2018) | 1 line Changes for Windows including making paths look prettier using backslash or slash more consistently, adding some tests for empty lists, setting up nyquist directory using Registry's XLISPPATH. ------------------------------------------------------------------------ r272 | rbd | 2018-08-31 14:12:12 -0500 (Fri, 31 Aug 2018) | 2 lines Everything seems to be working on OS X. Latest fix was to Sound Browser. ------------------------------------------------------------------------ r271 | rbd | 2018-08-31 13:00:54 -0500 (Fri, 31 Aug 2018) | 2 lines Adapting to Java 10, fixing sound browser in the face of unloaded extensions. ------------------------------------------------------------------------ r270 | rbd | 2018-08-30 13:18:11 -0500 (Thu, 30 Aug 2018) | 2 lines Installer code sets registry to user-selected nyquist directory. ------------------------------------------------------------------------ r269 | rbd | 2018-08-30 12:53:21 -0500 (Thu, 30 Aug 2018) | 1 line Getting ready to releasea 3.14. These changes are from Windows. ------------------------------------------------------------------------ r268 | rbd | 2018-08-29 20:06:41 -0500 (Wed, 29 Aug 2018) | 2 lines Changes for simpler installation with nyquist (lib, doc, runtime, demos) in user read/write file space. ------------------------------------------------------------------------ r267 | rbd | 2018-08-26 20:45:14 -0500 (Sun, 26 Aug 2018) | 2 lines Added some documentation, worked on browser #anchor urls. ------------------------------------------------------------------------ r266 | rbd | 2018-08-22 18:27:01 -0500 (Wed, 22 Aug 2018) | 2 lines Fixed some extensions to deal with piano, which now autoloads. ------------------------------------------------------------------------ r265 | rbd | 2018-08-22 12:18:40 -0500 (Wed, 22 Aug 2018) | 2 lines added one more missing file for v3.13 ------------------------------------------------------------------------ r264 | rbd | 2018-08-22 10:54:15 -0500 (Wed, 22 Aug 2018) | 2 lines left out piano/autoload.lsp, needed for v3.13 ------------------------------------------------------------------------ r263 | rbd | 2018-08-22 10:09:16 -0500 (Wed, 22 Aug 2018) | 2 lines More cleanup for 3.13 release ------------------------------------------------------------------------ r262 | rbd | 2018-08-22 09:54:45 -0500 (Wed, 22 Aug 2018) | 2 lines More changes for 3.13 including status dialog while downloading extensions. ------------------------------------------------------------------------ r261 | rbd | 2018-08-19 21:34:23 -0500 (Sun, 19 Aug 2018) | 2 lines Updated documentation for v3.13 ------------------------------------------------------------------------ r260 | rbd | 2018-08-19 11:14:07 -0500 (Sun, 19 Aug 2018) | 2 lines Converted to using Extension Manager ------------------------------------------------------------------------ r259 | rbd | 2018-05-20 19:08:34 -0500 (Sun, 20 May 2018) | 2 lines Forgot to commit this new file ------------------------------------------------------------------------ r258 | rbd | 2018-03-10 12:11:22 -0600 (Sat, 10 Mar 2018) | 2 lines Substantial changes to pattern classes in xm.lsp to fix bug. The problems were obscure and unnoticed for a long time, but this new implementation is a major rewrite. I still need to test online course examples to make sure nothing is broken there. Otherwise, my assumption is there are few users and incompatibilities will not matter, so this is regarded as a minor bug fix. Also in this commit is some work on turning libraries and demos into Nyquist extensions supported by an extension manager within NyquistIDE. ------------------------------------------------------------------------ r257 | rbd | 2018-01-21 14:40:58 -0600 (Sun, 21 Jan 2018) | 2 lines Built nyquist and NyquistIDE on Ubuntu 14.04 LTS and using Oracle Java 9. Minor changes (former build specified Java version 1.7). ------------------------------------------------------------------------ r256 | rbd | 2017-09-24 14:05:06 -0500 (Sun, 24 Sep 2017) | 2 lines Fix type-checking bug in score-voice ------------------------------------------------------------------------ r255 | rbd | 2017-09-24 10:35:08 -0500 (Sun, 24 Sep 2017) | 2 lines Updated nyqrelide.iss for Roger's newer windows laptop ------------------------------------------------------------------------ r254 | rbd | 2017-09-24 10:27:05 -0500 (Sun, 24 Sep 2017) | 2 lines After testing on Windows 7, removed Test button from IDE, made fft_demo not depend so much on XLISPPATH to find pmorales/*.lsp ------------------------------------------------------------------------ r253 | rbd | 2017-09-23 18:44:41 -0500 (Sat, 23 Sep 2017) | 1 line Changes to compile on MSVS Community 2015 ------------------------------------------------------------------------ r252 | rbd | 2017-09-23 17:01:44 -0500 (Sat, 23 Sep 2017) | 2 lines A few adjustments made to allow examples.sal to run even if "demos" is not on the XLISPPATH ------------------------------------------------------------------------ r251 | rbd | 2017-09-23 16:14:27 -0500 (Sat, 23 Sep 2017) | 2 lines Minor changes to make this work on Ubuntu linux (or maybe any other linux) ------------------------------------------------------------------------ r250 | rbd | 2017-09-23 14:32:52 -0500 (Sat, 23 Sep 2017) | 2 lines Fixed version number in IDE About box ------------------------------------------------------------------------ r249 | rbd | 2017-09-23 14:17:16 -0500 (Sat, 23 Sep 2017) | 2 lines More fixes for 3.12. OS X version setting up symbolic links had bugs. ------------------------------------------------------------------------ r248 | rbd | 2017-09-23 00:31:29 -0500 (Sat, 23 Sep 2017) | 2 lines Processed documentation for v3.12 ------------------------------------------------------------------------ r247 | rbd | 2017-09-22 21:59:18 -0500 (Fri, 22 Sep 2017) | 2 lines Preparing v3.12. ------------------------------------------------------------------------ r246 | rbd | 2017-09-05 09:27:29 -0500 (Tue, 05 Sep 2017) | 1 line Fixes for Win10 and many changes to rounding throughout to avoid truncating to 32-bit ints when 64-bits might be available on some architectures. In particular, round() which returns double has been replaced by ROUND32, returning int, and ROUNDBIG returning intptr_t. ------------------------------------------------------------------------ r245 | rbd | 2017-08-24 11:15:22 -0500 (Thu, 24 Aug 2017) | 2 lines Fixed bug affecting Linux: seq-write-smf was trying to close file twice causing nyquist crash. Fixed by adding intermediate helper function in seqinterf.c so SEQ-WRITE-SMF does not directly call seq_write_smf. ------------------------------------------------------------------------ r244 | rbd | 2016-11-02 13:33:49 -0500 (Wed, 02 Nov 2016) | 2 lines Forgot to include this in the repo ------------------------------------------------------------------------ r243 | rbd | 2016-10-11 12:23:21 -0500 (Tue, 11 Oct 2016) | 1 line Prepare for v3.11b ------------------------------------------------------------------------ r242 | rbd | 2016-10-11 12:19:31 -0500 (Tue, 11 Oct 2016) | 2 lines Improved documentation and formatting. This is for version 3.11b (64-bit Windows only) ------------------------------------------------------------------------ r241 | rbd | 2016-10-11 10:59:51 -0500 (Tue, 11 Oct 2016) | 2 lines Fixed bug in fftlib.c which took the negative of an unsigned long. ------------------------------------------------------------------------ r240 | rbd | 2016-10-06 15:49:51 -0500 (Thu, 06 Oct 2016) | 2 lines Fixed spec-plot parameter checking, which was totally broken before. ------------------------------------------------------------------------ r239 | rbd | 2016-10-06 15:23:22 -0500 (Thu, 06 Oct 2016) | 2 lines Better conversion into Latex and pdf manual. Other minor fixes. ------------------------------------------------------------------------ r238 | rbd | 2016-08-25 13:01:45 -0500 (Thu, 25 Aug 2016) | 1 line Adding readme for top level download folder on SourceForge. ------------------------------------------------------------------------ r237 | rbd | 2016-08-25 09:07:08 -0500 (Thu, 25 Aug 2016) | 1 line Install is now for x64 architecture. Fixed cell_aut demo to run in IDE sound browser. Modified NyquistThread to create default XLISPPATH from Nyquist dir instead of cwd. ------------------------------------------------------------------------ r236 | rbd | 2016-08-24 21:25:56 -0500 (Wed, 24 Aug 2016) | 2 lines minor change during release from OS X ------------------------------------------------------------------------ r235 | rbd | 2016-08-24 21:16:55 -0500 (Wed, 24 Aug 2016) | 2 lines Making new release ------------------------------------------------------------------------ r234 | rbd | 2016-08-24 21:05:22 -0500 (Wed, 24 Aug 2016) | 2 lines Adding missing file to repo. ------------------------------------------------------------------------ r233 | rbd | 2016-08-24 09:23:39 -0500 (Wed, 24 Aug 2016) | 1 line Fixed undefined freeimage problem by moving freeimage out of the conditional compilation. freeimage is defined so that when Nyquist shuts down it can free most of the memory it allocated, minimizing (false positive) reports of memory leaks, which are harmless (except that they obscure true positive messages and seem surprising to non-xlisp experts). Undefined SAVERESTORE flag the way it used to be because save and restore functions (other than freeimage) do not work with the Nyquist SOUND type extension to XLISP. ------------------------------------------------------------------------ r232 | rbd | 2016-07-19 09:39:45 -0500 (Tue, 19 Jul 2016) | 2 lines I was going to keep "debug" versions separate because of the runtime overhead, but the code started to diverge and it's hard to maintain two versions, so we're going to make all the type checking standard. If you want an optimized version, you could strip out the calls to ny:typecheck using list processing and it would probably work. You could even write NY:OPTIMIZE that would poke into all the function definitions and destructively edit the code. ------------------------------------------------------------------------ r231 | rbd | 2016-07-19 09:34:16 -0500 (Tue, 19 Jul 2016) | 2 lines moved to test/ ------------------------------------------------------------------------ r230 | rbd | 2016-07-19 09:32:11 -0500 (Tue, 19 Jul 2016) | 2 lines Added extensive type checking to nyquist built-in (but not primitive) functions, and added unit tests for type checking, and incorporated unit tests into regression-test.lsp, a new "grand" test sequence. ------------------------------------------------------------------------ r229 | rbd | 2016-06-23 14:28:03 -0500 (Thu, 23 Jun 2016) | 2 lines New code to build NyquistIDE.app -- the old appbundler stuff is no longer needed ------------------------------------------------------------------------ r228 | rbd | 2016-06-23 06:44:49 -0500 (Thu, 23 Jun 2016) | 2 lines Appbundler is Oracle software used to build NyquistIDE - originally from java.net, but it seems to be not an active project (maybe that is good) so let's keep a copy just in case. ------------------------------------------------------------------------ r227 | rbd | 2016-06-22 16:02:26 -0500 (Wed, 22 Jun 2016) | 2 lines previous change to snd_list_unref was VERY wrong; here is the fix. ------------------------------------------------------------------------ r226 | rbd | 2016-06-22 13:46:44 -0500 (Wed, 22 Jun 2016) | 2 lines inserted some memory-freeing code from upstream (Audacity); made snd_list_unref non-recursive; CMakeLists.txt was not working on Mac 10.11.5 -- still can't build NyquistIDE, so more changes are on the way ------------------------------------------------------------------------ r225 | rbd | 2016-05-11 10:54:16 -0500 (Wed, 11 May 2016) | 2 lines Overhaul of new typechecking and parameter validation and error reporting code. Mostly untested, but tests to be done are in typechecks.sal ------------------------------------------------------------------------ r224 | rbd | 2016-05-06 07:56:11 -0500 (Fri, 06 May 2016) | 2 lines Improved plot in nyqide, progress on validating parameters for SAL, spec-plot defined, autoload spec-plot and piano synthesis functions ------------------------------------------------------------------------ r223 | rbd | 2016-05-03 13:07:14 -0500 (Tue, 03 May 2016) | 2 lines This contains work in progress on validating parameters in many nyquist functions, fixing liblo for win64, and some security (integer overflow) problems - probably does not compile ------------------------------------------------------------------------ r222 | rbd | 2016-03-16 10:34:15 -0500 (Wed, 16 Mar 2016) | 2 lines Updated documentation with minor additions and corrections. ------------------------------------------------------------------------ r221 | rbd | 2016-01-15 18:59:45 -0600 (Fri, 15 Jan 2016) | 2 lines Fixed file name for Browser ------------------------------------------------------------------------ r220 | rbd | 2016-01-15 18:49:15 -0600 (Fri, 15 Jan 2016) | 2 lines Changes to fix compilation on OS X ------------------------------------------------------------------------ r219 | rbd | 2016-01-15 18:31:23 -0600 (Fri, 15 Jan 2016) | 2 lines fixed build code for linux, made some c code more compatible ------------------------------------------------------------------------ r218 | rbd | 2016-01-14 17:08:39 -0600 (Thu, 14 Jan 2016) | 1 line modifications from Windows 7 port (static libraries) and browser files pointed to demos/src new location. ------------------------------------------------------------------------ r217 | rbd | 2016-01-14 13:04:24 -0600 (Thu, 14 Jan 2016) | 2 lines Now that cmake is in use, we do not need old Makefiles ------------------------------------------------------------------------ r216 | rbd | 2016-01-11 02:00:08 -0600 (Mon, 11 Jan 2016) | 1 line fixed to compile on XCode ------------------------------------------------------------------------ r215 | rbd | 2016-01-11 01:32:58 -0600 (Mon, 11 Jan 2016) | 1 line examples tried to play drums, but they may not be installed ------------------------------------------------------------------------ r214 | rbd | 2016-01-11 01:05:00 -0600 (Mon, 11 Jan 2016) | 1 line more win7 fixes ------------------------------------------------------------------------ r213 | rbd | 2016-01-11 00:42:57 -0600 (Mon, 11 Jan 2016) | 2 lines more file and path problems fixed ------------------------------------------------------------------------ r212 | rbd | 2016-01-11 00:19:55 -0600 (Mon, 11 Jan 2016) | 2 lines fixed lpc demo ------------------------------------------------------------------------ r211 | rbd | 2016-01-10 23:45:50 -0600 (Sun, 10 Jan 2016) | 1 line fix read-float, restore pianosyn.lsp ------------------------------------------------------------------------ r210 | rbd | 2016-01-10 23:36:35 -0600 (Sun, 10 Jan 2016) | 1 line fix references to demo-snd.aiff which is in demos/audio now ------------------------------------------------------------------------ r209 | rbd | 2016-01-10 22:32:08 -0600 (Sun, 10 Jan 2016) | 1 line getting 3.10 to run on windows, has temp debug info in pianosyn.lsp ------------------------------------------------------------------------ r208 | rbd | 2016-01-10 20:49:37 -0600 (Sun, 10 Jan 2016) | 2 lines Forgot to add new demo files ------------------------------------------------------------------------ r207 | rbd | 2016-01-10 20:31:40 -0600 (Sun, 10 Jan 2016) | 2 lines fixed downsample.c, other minor release problems ------------------------------------------------------------------------ r206 | rbd | 2016-01-10 14:01:58 -0600 (Sun, 10 Jan 2016) | 2 lines missing files (maybe only cmupvdbg.h is really needed) ------------------------------------------------------------------------ r205 | rbd | 2016-01-10 12:10:01 -0600 (Sun, 10 Jan 2016) | 2 lines No more scribe, so no more auto generation of text-only documentation. Hopefully HTML will suffice. PDF is now done by latex (but I forgot to move it into the doc directory before). ------------------------------------------------------------------------ r204 | rbd | 2016-01-10 12:01:55 -0600 (Sun, 10 Jan 2016) | 2 lines Nearing release of 3.10 -- major changes are phasevocoder, convolution, demo documentation ------------------------------------------------------------------------ r203 | rbd | 2016-01-10 11:38:35 -0600 (Sun, 10 Jan 2016) | 2 lines Made revisions for 3.10. Not quite done yet. ------------------------------------------------------------------------ r202 | rbd | 2015-12-28 22:33:51 -0600 (Mon, 28 Dec 2015) | 2 lines Updating demos to SAL syntax and better "front page". ------------------------------------------------------------------------ r201 | rbd | 2015-05-20 19:46:16 -0500 (Wed, 20 May 2015) | 2 lines Fixed more warnings about types. Still working on convolve though. ------------------------------------------------------------------------ r200 | rbd | 2015-05-20 06:16:32 -0500 (Wed, 20 May 2015) | 2 lines Forgot to move the final pdf of the manual to doc. ------------------------------------------------------------------------ r199 | rbd | 2015-05-20 06:14:25 -0500 (Wed, 20 May 2015) | 2 lines Fixed some documentation -- it might be complete now. Removed extra play command from organ.lsp. ------------------------------------------------------------------------ r198 | rbd | 2015-05-19 21:32:24 -0500 (Tue, 19 May 2015) | 2 lines Fixed some problems and warnings when compiling on Ubuntu Linux ------------------------------------------------------------------------ r197 | rbd | 2015-05-19 20:55:04 -0500 (Tue, 19 May 2015) | 2 lines Merged Win32 changes with OS X, removed some .sln and .vcproj files that are not needed now that we are using CMake ------------------------------------------------------------------------ r196 | rbd | 2015-05-19 20:27:30 -0500 (Tue, 19 May 2015) | 1 line Got nyquist and jnyqide to build on Windows under Visual Studio Express 2013. Many changes to remove some compiler warnings. ------------------------------------------------------------------------ r195 | rbd | 2015-05-14 21:52:33 -0500 (Thu, 14 May 2015) | 2 lines better device selection for ALSA and added cross-platform help to find and select devices; added icon to jny on Linux (should work on Windows) ------------------------------------------------------------------------ r194 | rbd | 2015-05-12 21:08:59 -0500 (Tue, 12 May 2015) | 2 lines still more missing files ------------------------------------------------------------------------ r193 | rbd | 2015-05-12 21:07:41 -0500 (Tue, 12 May 2015) | 2 lines more missing files ------------------------------------------------------------------------ r192 | rbd | 2015-05-12 21:04:50 -0500 (Tue, 12 May 2015) | 2 lines another missing file ------------------------------------------------------------------------ r191 | rbd | 2015-05-12 20:56:39 -0500 (Tue, 12 May 2015) | 2 lines repo missing a file ------------------------------------------------------------------------ r190 | rbd | 2015-05-12 20:28:25 -0500 (Tue, 12 May 2015) | 2 lines repo is missing a file ------------------------------------------------------------------------ r189 | rbd | 2015-05-12 20:16:10 -0500 (Tue, 12 May 2015) | 2 lines cleaned out files we don't use ------------------------------------------------------------------------ r188 | rbd | 2015-05-12 12:42:57 -0500 (Tue, 12 May 2015) | 2 lines Updated liblo to v0.28 and tested that it compiles with nyquist on XCode ------------------------------------------------------------------------ r187 | rbd | 2015-05-12 07:00:06 -0500 (Tue, 12 May 2015) | 2 lines FLAC changes were incomplete. This revision compiles. ------------------------------------------------------------------------ r186 | rbd | 2015-05-12 06:39:06 -0500 (Tue, 12 May 2015) | 2 lines Updated FLAC to 1.3.1, removed unnecessary files from local copy of sources. ------------------------------------------------------------------------ r185 | rbd | 2015-05-11 22:51:11 -0500 (Mon, 11 May 2015) | 2 lines I updated libvorbis from 1.3.5 and deleted unneeded files ------------------------------------------------------------------------ r184 | rbd | 2015-05-11 22:29:41 -0500 (Mon, 11 May 2015) | 2 lines I decided to just keep sources in libraries that are needed to build Nyquist, so I'm removing lots of build and configuration files. So far, portaudio is updated to V19 and libogg is updated to 1.3.2 ------------------------------------------------------------------------ r183 | rbd | 2015-05-11 15:00:14 -0500 (Mon, 11 May 2015) | 2 lines macosxproject/CMakeLists.txt is now in place to generate nyquist.xcodeproj. Since it is generated by CMake, I removed it from the repo. ------------------------------------------------------------------------ r182 | rbd | 2015-05-11 14:03:53 -0500 (Mon, 11 May 2015) | 2 lines Added sliders, documentation is now in latex, new Java build, CMake is used to create project in OS X, many other changes. This is NOT a release! Next step will be to get the CMake-built project into the right name and directory. ------------------------------------------------------------------------ r181 | rbd | 2015-03-19 19:40:00 -0500 (Thu, 19 Mar 2015) | 2 lines This is a failed attempt to use PanDoc to make a Nyquist manual, now that Scribe seems to have died. This is the point where I am changing the code to write directly to Latex. ------------------------------------------------------------------------ r180 | rbd | 2015-03-18 10:17:44 -0500 (Wed, 18 Mar 2015) | 2 lines Update version numbers to 3.10 ------------------------------------------------------------------------ r179 | rbd | 2015-03-17 20:17:35 -0500 (Tue, 17 Mar 2015) | 2 lines minor bug fixes ------------------------------------------------------------------------ r178 | rbd | 2015-03-17 20:05:13 -0500 (Tue, 17 Mar 2015) | 1 line Various changes from Windows version.res
1891 lines
57 KiB
C
1891 lines
57 KiB
C
/****************************************************************************
|
||
seqread.c -- Phase 1 of adagio compilation...
|
||
|
||
this module parses adagio programs and builds a linked list structure
|
||
consisting of notes and control changes in time order.
|
||
|
||
Copyright 1989 Carnegie Mellon University
|
||
*****************************************************************************/
|
||
|
||
/*****************************************************************************
|
||
* Change Log
|
||
* Date | Change
|
||
*-----------+-----------------------------------------------------------------
|
||
* 31-Dec-85 | Created changelog
|
||
* 31-Dec-85 | Add c:\ to include directives
|
||
* 31-Dec-85 | Added standard command scanner, metronome variable, need to add
|
||
* | cmdline_help procedure
|
||
* 31-Dec-85 | Call intr_init
|
||
* 31-Dec-85 | Set musictrace from command line via -trace
|
||
* 31-Dec-85 | Added -poll
|
||
* 1-Jan-86 | Put error messages out to stderr
|
||
* 1-Jan-86 | Set IsAT. Can be later overridden by -at and -xt switches,
|
||
* | currently only used for diagnostics (may be needed for
|
||
* | compatibles, who knows? In which case remove the tests which
|
||
* | confirm the type of processor)
|
||
* 1-Jan-86 | <rgd/jmn> Removed dur-adjusted message
|
||
* 1-Jan-86 | Added miditrace
|
||
* 18-Jan-86 | Shortened durations by 1/200 s to avoid roundoff problems --
|
||
* | see buildnote for details.
|
||
* 3-Mar-86 | Allow octave and accidentals in either order after pitch name.
|
||
* | Default octave is now one that gets nearest previous pitch,
|
||
* | the tritone (half an octave) interval is descending by default.
|
||
* | Special commands handled by table search, !Rate command added
|
||
* | to scale all times by a percentage (50 = half speed).
|
||
* 9-Mar-86 | Use space to limit amount of storage allocation. Otherwise
|
||
* | exhausting storage in phase1 caused phase2 to fail.
|
||
* 12-Mar-86 | Broke off command line parser into adagio.c, only parser remains
|
||
* 24-Mar-86 | Changed representation from note_struct to event_struct
|
||
* | Parse M, N, O, X, and Y as control change commands
|
||
* 23-May-86 | Added , and ; syntax: "," means "N0\n", ";" means "\n"
|
||
* 16-Jul-86 | modify to only call toupper/lower with upper/lower case as
|
||
* | parameter to be compatible with standard C functions
|
||
* 7-Aug-86 | fixed bug with default pitches and rests
|
||
* 5-Jul-87 | F.H: Introduced new memory handling from Mac version.
|
||
* | Changed: init()
|
||
* | ins_event()
|
||
* | ins_ctrl()
|
||
* | ins_note()
|
||
* | Deleted: reverse()
|
||
* | nalloc()
|
||
* | Introduced: event_alloc()
|
||
* | phase1_FreeMem()
|
||
* | system.h & system.c dependencies
|
||
* 10-Feb-88 | fixed parseend to accept blanks and tabs,
|
||
* | fixed rate scaling of durations
|
||
* 11-Jun-88 | commented out gprintf of \n to ERROR after parsing finished.
|
||
* 13-Oct-88 | JCD : exclusive AMIGA version.
|
||
* 13-Apr-89 | JCD : New portable version.
|
||
* 31-Jan-90 | GWL : Cleaned up for LATTICE
|
||
* 30-Jun-90 | RBD : further changes
|
||
* 2-Apr-91 | JDW : further changes
|
||
* 30-Jun-91 | RBD : parse '+' and '/' in durations, * after space is comment
|
||
* 28-Apr-03 | DM : changes for portability
|
||
*****************************************************************************/
|
||
|
||
#include "switches.h"
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#include "cext.h"
|
||
#include "cmdline.h"
|
||
#include "midifns.h" /* to get time_type */
|
||
#include "timebase.h"
|
||
#include "moxc.h" /* to get debug declared */
|
||
#include "seq.h"
|
||
#include "seqread.h"
|
||
#include "userio.h"
|
||
/* ctype.h used to be included only by UNIX and AMIGA,
|
||
surely everyone wants this? */
|
||
#include "ctype.h"
|
||
|
||
#ifndef toupper
|
||
/* we're already taking precautions, so inline version of toupper is ok: */
|
||
#define toupper(c) ((c)-'a'+'A')
|
||
/* CAUTION: AZTEC V5.0 defines an inline version of toupper called _toupper,
|
||
but they got it wrong!
|
||
*/
|
||
#endif
|
||
|
||
/* cmtcmd.h references amiga message ports */
|
||
#ifdef AMIGA
|
||
#ifdef LATTICE
|
||
#include "amiga.h"
|
||
#endif
|
||
#include "exec/exec.h"
|
||
#endif
|
||
#include "cmtcmd.h"
|
||
|
||
/* public stuff */
|
||
extern long space; /* remaining free bytes */
|
||
extern int abort_flag;
|
||
|
||
/****************************************************************************
|
||
The following are used to simulate fixed point with the radix point
|
||
8 bits from the right:
|
||
****************************************************************************/
|
||
|
||
#define precise(x) (((time_type) x) << 8)
|
||
#define seqround(x) ((((time_type) x) + 128) >> 8)
|
||
#define trunc(x) (((time_type) x) >> 8)
|
||
|
||
#define nullstring(s) (s[0] == EOS)
|
||
|
||
|
||
/****************************************************************************
|
||
* Routines local to this module:
|
||
****************************************************************************/
|
||
private void do_a_rest();
|
||
private time_type doabsdur();
|
||
private int doabspitch();
|
||
private void doclock();
|
||
private void docomment();
|
||
private void doctrl();
|
||
private void dodef();
|
||
private time_type dodur();
|
||
private void doerror();
|
||
private int doloud();
|
||
void domacro();
|
||
private void donextdur();
|
||
private int dopitch();
|
||
private void doprogram();
|
||
private void dorate();
|
||
private void doset();
|
||
private void dospecial();
|
||
private time_type dosymdur();
|
||
private void dotempo();
|
||
private void dotime();
|
||
private void dovoice();
|
||
private void fferror();
|
||
private void init();
|
||
private int issymbol();
|
||
private void marker();
|
||
private void parseend();
|
||
private void parsefield();
|
||
private boolean parsenote();
|
||
private boolean parseparm();
|
||
private int scan();
|
||
private int scan1();
|
||
private long scanint();
|
||
private void scansymb();
|
||
private long scansgnint();
|
||
|
||
/****************************************************************************
|
||
* data structures for parser lookup tables
|
||
****************************************************************************/
|
||
|
||
struct durt { /* duration translation table */
|
||
char symbol;
|
||
time_type value;
|
||
};
|
||
|
||
#define durtable_len 7
|
||
struct durt durtable[durtable_len] = {
|
||
{'W', 4800L},
|
||
{'H', 2400L},
|
||
{'Q', 1200L},
|
||
{'I', 600L},
|
||
{'S', 300L},
|
||
{'%', 150L},
|
||
{'^', 75L}
|
||
};
|
||
|
||
struct loudt { /* loudness translation table */
|
||
char symbol[4];
|
||
int value;
|
||
};
|
||
|
||
struct loudt loudtable[] = {
|
||
{"PPP", 20},
|
||
{"PP\0", 26},
|
||
{"P\0\0", 34},
|
||
{"MP\0", 44},
|
||
{"MF\0", 58},
|
||
{"F\0\0", 75},
|
||
{"FF\0", 98},
|
||
{"FFF", 127}
|
||
};
|
||
|
||
char too_many_error[] = "Too many parameters";
|
||
|
||
private char *ssymbols[] = {"TEMPO", "RATE", "CSEC", "MSEC",
|
||
"SETI", "SETV", "CALL", "RAMP",
|
||
"CLOCK", "DEF", "END"};
|
||
|
||
#define sym_tempo 0
|
||
#define sym_rate 1
|
||
#define sym_csec 2
|
||
#define sym_msec 3
|
||
#define sym_seti 4
|
||
#define sym_setv 5
|
||
#define sym_call 6
|
||
#define sym_ramp 7
|
||
#define sym_clock 8
|
||
#define sym_def 9
|
||
#define sym_end 10
|
||
|
||
/* number of symbols */
|
||
#define sym_n 11
|
||
|
||
#define linesize 100
|
||
private char line[linesize]; /* the input line */
|
||
private char token[linesize]; /* a token scanned from the input line */
|
||
|
||
private boolean pitch_flag; /* set when a pitch is indicated */
|
||
/* (if controls changes are given, only allocate a note event if
|
||
* a pitch was specified -- i.e. when pitch_flag is set)
|
||
*/
|
||
private boolean rest_flag; /* set when a rest (R) is found */
|
||
/* this flag is NOT inherited by the next line */
|
||
|
||
private boolean symbolic_dur_flag;
|
||
/* TRUE if last dur was not absolute
|
||
* (if this is set, then the default duration is changed
|
||
* accordingly when the tempo is changed.)
|
||
*/
|
||
|
||
|
||
#define nctrl 8
|
||
|
||
private boolean ctrlflag[nctrl];
|
||
/* TRUE if control change was present
|
||
* ctrlflag[0] TRUE if ANY control change
|
||
* was present
|
||
*/
|
||
private int ctrlval[nctrl];
|
||
/* the new value of the control */
|
||
#define nmacroctrl 10
|
||
short macctrlx; /* index into the following: */
|
||
short macctrlnum[nmacroctrl]; /* macro ctrl number, e.g. for ~4(67), or
|
||
* number of parameters for a symbolic macro */
|
||
short macctrlparmx[nmacroctrl]; /* ctrl value for ctrl change, or index of
|
||
* parameters for symbolic macro */
|
||
short macctrlparms[nmacroctrl*nmacroparms]; /* parameters for symbolic macros */
|
||
short macctrlnextparm;
|
||
def_type macctrldef[nmacroctrl]; /* definition for symbolic macro */
|
||
|
||
private time_type time_scale; /* 1000 if centisec, 100 if millisec */
|
||
/* note: user_specified_time * (time_scale / rate) = millisec */
|
||
|
||
|
||
|
||
/****************************************************************************
|
||
*
|
||
* variables private to this module
|
||
*
|
||
****************************************************************************/
|
||
|
||
private boolean end_flag = FALSE; /* set "true" when "!END" is seen */
|
||
|
||
/****************************************************************************
|
||
* state variables
|
||
* Because each line of an Adagio score inherits properties from the previous
|
||
* line, it makes sense to implement the parser as a collection of routines
|
||
* that make small changes to some global state. For example, pitch is a
|
||
* global variable. When the field G4 is encountered, the dopitch routine
|
||
* assigns the pitch number for G4 to the variable pitch. After all fields
|
||
* are processed, these variables describe the current note and contain the
|
||
* default parameters for the next note as well.
|
||
*
|
||
* Global variables that are used in this way by the parsing rountines are:
|
||
****************************************************************************/
|
||
private int
|
||
linex, /* index of the next character to be scanned */
|
||
lineno, /* current line number */
|
||
fieldx, /* index of the current character within a field */
|
||
pitch, /* pitch of note */
|
||
loud, /* loudness of note */
|
||
voice, /* voice (midi channel) of note */
|
||
artic; /* articulation (a percentage of duration) */
|
||
|
||
private boolean ndurp; /* set when a next (N) is indicated */
|
||
/* (next time defaults to the current time plus duration unless
|
||
* overridden by a next (N) command whose presence is signalled
|
||
* by ndurp.)
|
||
*/
|
||
|
||
private time_type
|
||
thetime, /* the starting time of the note */
|
||
rate, /* time rate -- scales time and duration, default = 100 */
|
||
ntime, /* the starting time of the next note */
|
||
dur, /* the duration of the note */
|
||
tempo, /* the current tempo */
|
||
start, /* the reference time (time of last !tempo or !rate cmd) */
|
||
ticksize; /* set by !clock command, zero for no clock */
|
||
|
||
private int pitchtable[7] = {
|
||
69, 71, 60, 62, 64, 65, 67 };
|
||
|
||
extern char score_na[name_length];
|
||
|
||
private seq_type the_score; /* this is the score we are parsing */
|
||
|
||
|
||
/* def_append -- append a byte to the current definition */
|
||
/*
|
||
* The def data structure:
|
||
* [code][offset][code][offset]...[0][length][data][data][data]...
|
||
* where code is 1:nmacroparms for %n,
|
||
* nmacroparms+1 for %v,
|
||
* nmacroparms+2:nmacroparms*2+1 for ^n
|
||
* and offset is the byte offset (from the offset byte) to the data
|
||
* where the parameter should be substituted
|
||
* and length is the number of data bytes
|
||
*/
|
||
boolean def_append(def, nparms, data)
|
||
unsigned char def[];
|
||
int nparms;
|
||
int data;
|
||
{
|
||
int base = (nparms << 1) + 1; /* this byte is the length */
|
||
/* first parameter has to be able to reference last byte: */
|
||
if ((def[base])++ >= (254 - (nparms << 1))) {
|
||
fferror("Data too long");
|
||
return FALSE;
|
||
}
|
||
def[base + def[base]] = data;
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
def_type def_lookup(symbol)
|
||
char *symbol;
|
||
{
|
||
def_type defn = seq_dictionary(the_score);
|
||
while (defn) {
|
||
if (strcmp(defn->symbol, symbol) == 0) {
|
||
return defn;
|
||
}
|
||
defn = defn->next;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
|
||
void def_parm(def, nparms, code)
|
||
unsigned char def[];
|
||
int nparms;
|
||
int code;
|
||
{
|
||
int i, j;
|
||
/* in order to insert a 2-byte parameter descriptor, the offsets from
|
||
* previous descriptors (that precede the data) need to be increased by 2:
|
||
*/
|
||
for (i = 1; i < (nparms << 1); i += 2) {
|
||
def[i] += 2;
|
||
}
|
||
/* now i is index of length; work backwards from the last byte, moving
|
||
* everything up by 2 bytes to make room for the new descriptor:
|
||
*/
|
||
for (j = i + def[i] + 2; j > i; j--) {
|
||
def[j] = def[j - 2];
|
||
}
|
||
/* now i is index of offset; insert the descriptor code (first byte)
|
||
* and the offset to the parameter location in the message (second byte)
|
||
*/
|
||
def[i - 1] = code;
|
||
def[i] = def[i + 2] + 2;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* do_a_rest
|
||
* Effect: parses a rest (R) command
|
||
****************************************************************************/
|
||
|
||
private void do_a_rest()
|
||
{
|
||
if (token[fieldx])
|
||
fferror("Nothing expected after rest");
|
||
rest_flag = TRUE;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* doabsdur
|
||
* Effect: parses an absolute dur (U) command
|
||
****************************************************************************/
|
||
|
||
private time_type doabsdur()
|
||
{
|
||
time_type result=1000L;
|
||
register char c;
|
||
if (isdigit(token[fieldx])) {
|
||
result = precise(scanint());
|
||
/* allow comma or paren for use in parameter lists */
|
||
if ((c = token[fieldx]) && (c != ',') && (c != ')') && (c != '+')) {
|
||
fferror("U must be followed by digits only");
|
||
}
|
||
if (time_scale == 1000) result *= 10; /* convert to ms */
|
||
} else fferror("No digit after U");
|
||
return result;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* doabspitch
|
||
* Effect: parses an absolute pitch (P) command
|
||
****************************************************************************/
|
||
|
||
private int doabspitch()
|
||
{
|
||
int result = 60;
|
||
int startx = fieldx;
|
||
register char c;
|
||
int savex;
|
||
if (isdigit (token[fieldx])) {
|
||
result = (int) scanint();
|
||
/* allow comma or paren for abspitch in parameter */
|
||
if ((c = token[fieldx]) && c != ',' && c != ')')
|
||
fferror("P must be followed by digits only");
|
||
else if (result < minpitch) {
|
||
savex = fieldx;
|
||
fieldx = startx;
|
||
fferror("Minimum pitch of 0 will be used");
|
||
result = minpitch;
|
||
fieldx = savex;
|
||
} else if (result > maxpitch) {
|
||
savex = fieldx;
|
||
fieldx = startx;
|
||
fferror("Maximum pitch of 127 will be used");
|
||
result = maxpitch;
|
||
fieldx = savex;
|
||
}
|
||
} else fferror("No digits after P");
|
||
return result;
|
||
}
|
||
|
||
|
||
/* doartic -- compute an articulation factor */
|
||
/*
|
||
NOTE: artic is a percentage that scales the duration
|
||
of notes but not the time to the next note onset. It
|
||
is applied to the final computed duration after all
|
||
other scaling is applied.
|
||
*/
|
||
private void doartic()
|
||
{
|
||
if (isdigit(token[fieldx])) {
|
||
artic = (int) scanint();
|
||
if (token[fieldx])
|
||
fferror("Only digits were expected here");
|
||
} else fferror("No digits after /");
|
||
}
|
||
|
||
|
||
/* docall -- parse a call in the form !CALL fn(p1,p2,p3) */
|
||
/**/
|
||
private void docall()
|
||
{
|
||
boolean error_flag = TRUE;
|
||
ndurp = FALSE;
|
||
|
||
linex += scan();
|
||
|
||
if (token[0] == 0) fferror("Function name expected");
|
||
else {
|
||
char symbol[100];
|
||
struct symb_descr *desc;
|
||
long value[SEQ_MAX_PARMS];
|
||
int i=0;
|
||
|
||
scansymb(symbol);
|
||
if (fieldx == 1) fferror("Routine name expected");
|
||
else if (token[fieldx] != '(') fferror("Open paren expected");
|
||
else {
|
||
desc = &HASHENTRY(lookup(symbol));
|
||
if (!desc->symb_type) {
|
||
fieldx = 0;
|
||
fferror("Function not defined");
|
||
} else if (desc->symb_type != fn_symb_type) {
|
||
fieldx = 0;
|
||
gprintf(TRANS, "desc->symb_type is %d\n", desc->symb_type);
|
||
fferror("This is not a function");
|
||
} else {
|
||
error_flag = FALSE;
|
||
fieldx++; /* skip over paren */
|
||
for (i = 0; i < SEQ_MAX_PARMS; i++) value[i] = 0;
|
||
i = 0;
|
||
/* note that no params "()" is legal */
|
||
while (i < SEQ_MAX_PARMS && token[fieldx] != ')' &&
|
||
parseparm(&value[i])) {
|
||
i++;
|
||
if (token[fieldx] == ',') {
|
||
fieldx++;
|
||
} else if (token[fieldx] != ')') {
|
||
fferror("Unexpected character");
|
||
error_flag = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
fieldx++;
|
||
if (i > SEQ_MAX_PARMS) fferror("Too many parameters");
|
||
}
|
||
while (TRUE) {
|
||
linex += scan();
|
||
if (nullstring(token)) {
|
||
break;
|
||
}
|
||
switch (token[0]) {
|
||
case 'T':
|
||
fieldx = 1;
|
||
dotime();
|
||
break;
|
||
case 'V':
|
||
fieldx = 1;
|
||
dovoice();
|
||
break;
|
||
case 'N':
|
||
fieldx = 1;
|
||
donextdur();
|
||
break;
|
||
default:
|
||
fferror("Unexpected character");
|
||
}
|
||
}
|
||
if (!error_flag)
|
||
insert_call(the_score, seqround(thetime), lineno, voice,
|
||
desc->ptr.routine, value, i);
|
||
/* advance the time only if an N field was given */
|
||
if (ndurp) thetime += ntime;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* doclock -- insert a clock command */
|
||
/*
|
||
* derivation: if there is no previous clock running, then start the
|
||
* clock on time. Otherwise, start the clock half a tick early.
|
||
* ticksize = (beattime / 24) = ((60sec/tempo)/24) =
|
||
* ((60000ms/tempo)/24) = (60000/24)/tempo = 2500/tempo
|
||
*/
|
||
private void doclock()
|
||
{
|
||
int oldticksize = ticksize;
|
||
ticksize = (2500L << 16) / tempo;
|
||
insert_clock(the_score, seqround(thetime) - (oldticksize >> 17),
|
||
lineno, ticksize);
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* docomment
|
||
* Effect: parses a comment (*) command
|
||
****************************************************************************/
|
||
|
||
private void docomment()
|
||
{
|
||
line[linex] = '\n'; /* force end of line to skip comment line */
|
||
line[linex+1] = EOS;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* doctrl
|
||
* Inputs:
|
||
* n: control number
|
||
* Effect: parses a control (K, M, O, X, or Y) command
|
||
****************************************************************************/
|
||
|
||
private void doctrl(n)
|
||
int n;
|
||
{
|
||
ctrlval[n] = (int) scanint();
|
||
if (token[fieldx]) {
|
||
fferror("Only digits expected here");
|
||
} else {
|
||
ctrlflag[n] = TRUE;
|
||
ctrlflag[0] = TRUE; /* ctrlflag[0] set if any flag is set */
|
||
}
|
||
}
|
||
|
||
|
||
private void dodef()
|
||
{
|
||
/* maximum def size is 256 + 9 parms * 2 + 2 = 276 */
|
||
unsigned char def[280];
|
||
char symbol[100];
|
||
int nparms = 0;
|
||
int nibcount = 0;
|
||
int data = 0;
|
||
register char c;
|
||
|
||
linex += scan();
|
||
|
||
if (!token[0]) fferror("Symbol expected");
|
||
else {
|
||
strcpy(symbol, token);
|
||
def[0] = def[1] = 0;
|
||
while (TRUE) {
|
||
linex += scan1(&line[linex]);
|
||
c = token[0];
|
||
if (!c) {
|
||
linex--;
|
||
if (nibcount & 1) {
|
||
fferror("Expected pairs of hex digits: one missing");
|
||
return;
|
||
}
|
||
break;
|
||
} else if (c == ' ' || c == '\t' || c == '\n') continue;
|
||
else if (isdigit(c)) {
|
||
data = (data << 4) + (c - '0');
|
||
nibcount++;
|
||
if (!(nibcount & 1)) {
|
||
if (!def_append(def, nparms, data))
|
||
return;
|
||
data = 0;
|
||
}
|
||
} else if ('A' <= c && c <= 'F') {
|
||
data = (data << 4) + (c - 'A') + 10;
|
||
nibcount++;
|
||
if (!(nibcount & 1)) {
|
||
if (!def_append(def, nparms, data))
|
||
return;
|
||
data = 0;
|
||
}
|
||
} else if (c == 'V') {
|
||
data = data << 4;
|
||
nibcount++;
|
||
/* v without a leading nibble is equivalent to 0v: */
|
||
if (nibcount & 1) nibcount++;
|
||
if (!def_append(def, nparms, data))
|
||
return;
|
||
def_parm(def, nparms++, nmacroparms+1);
|
||
} else if (c == '%') {
|
||
linex += scan1(&line[linex]);
|
||
c = token[0];
|
||
if (c < '1' || c > ('0' + nmacroparms)) {
|
||
fferror(parm_expected_error);
|
||
break;
|
||
}
|
||
if (!def_append(def, nparms, 0))
|
||
return;
|
||
def_parm(def, nparms++, c - '0');
|
||
} else if (c == '^') {
|
||
linex += scan1(&line[linex]);
|
||
c = token[0];
|
||
if (c < '1' || c > ('0' + nmacroparms)) {
|
||
fferror(parm_expected_error);
|
||
break;
|
||
}
|
||
if (!def_append(def, nparms, 0))
|
||
return;
|
||
def_parm(def, nparms++, (c - '0') + nmacroparms + 1);
|
||
} else { /* something unexpected here -- just exit */
|
||
linex--;
|
||
fferror("Unexpected data");
|
||
return;
|
||
}
|
||
}
|
||
insert_def(the_score, symbol, def,
|
||
(nparms << 1) + def[(nparms << 1) + 1] + 2);
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* dodur
|
||
* Effect: parses a duration (sum of dosymdur and/or doabsdur)
|
||
* sets symbolic_dur_flag (according to the first addend in mixed arithmetic)
|
||
*
|
||
* Returns: duration in "precise" units
|
||
****************************************************************************/
|
||
private time_type dodur()
|
||
{
|
||
time_type result = 0L;
|
||
symbolic_dur_flag = TRUE;
|
||
|
||
if (token[fieldx-1] == 'U') {
|
||
result = doabsdur();
|
||
symbolic_dur_flag = FALSE;
|
||
} else result = dosymdur();
|
||
while (token[fieldx] == '+') {
|
||
fieldx += 2;
|
||
if (token[fieldx-1] == 'U') result += doabsdur();
|
||
else result += dosymdur();
|
||
}
|
||
return scale(result, 100L, rate);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* doerror
|
||
* Effect: parse an unrecognized field by reporting an error
|
||
****************************************************************************/
|
||
|
||
private void doerror()
|
||
{
|
||
fieldx = 0;
|
||
fferror("Bad field");
|
||
}
|
||
|
||
/****************************************************************************
|
||
* doloud
|
||
* Effect: parse a loudness (L) command
|
||
****************************************************************************/
|
||
|
||
private int doloud()
|
||
{
|
||
int i, j;
|
||
int result;
|
||
int oldfieldx = fieldx;
|
||
int newfieldx;
|
||
char symbol[100];
|
||
|
||
if (!token[fieldx] || token[fieldx]==')' || token[fieldx]==',') {
|
||
fferror("L must be followed by loudness indication");
|
||
return 100;
|
||
}
|
||
if (isdigit(token[fieldx])) {
|
||
result = (int) scanint();
|
||
newfieldx = fieldx;
|
||
if (token[fieldx] && token[fieldx]!=')' && token[fieldx]!=',')
|
||
fferror("Digits expected after L");
|
||
else if (result > 127) {
|
||
fieldx = oldfieldx;
|
||
fferror("Maximum loudness of 127 will be used");
|
||
fieldx = newfieldx;
|
||
result = 127;
|
||
} else if (result == 0) {
|
||
fieldx = oldfieldx;
|
||
fferror("Minimum loudness of 1 will be used");
|
||
fieldx = newfieldx;
|
||
result = 1;
|
||
}
|
||
return result;
|
||
}
|
||
scansymb(symbol);
|
||
newfieldx = fieldx;
|
||
if ((i = strlen(symbol)) > 3 ) { /* maximum is 3, e.g. "ppp" */
|
||
fieldx = oldfieldx;
|
||
fferror("Loudness field too long");
|
||
fieldx = newfieldx;
|
||
return 100;
|
||
}
|
||
symbol[i + 1] = '\0'; /* pad short symbols with 0 */
|
||
/* e.g. "p\0" -> "p\0\0" */
|
||
for (i = 0; i <= 7; i++) { /* loop through possibilities */
|
||
for (j = 0; j <= 2; j++) { /* test 3 characters */
|
||
if (symbol[j] != loudtable[i].symbol[j])
|
||
break;
|
||
}
|
||
if (j == 3) {
|
||
return loudtable[i].value;
|
||
}
|
||
}
|
||
fieldx = oldfieldx;
|
||
fferror("Bad loudness indication");
|
||
fieldx = newfieldx;
|
||
return 100;
|
||
}
|
||
|
||
|
||
void domacro()
|
||
{
|
||
int control_num;
|
||
int value;
|
||
if (isdigit(token[1])) {
|
||
control_num = (int) scanint();
|
||
if (token[fieldx] == '(') {
|
||
fieldx++;
|
||
if (!isdigit(token[fieldx])) {
|
||
fferror("Control value expected");
|
||
} else {
|
||
value = (int) scanint();
|
||
if (token[fieldx] != ')') {
|
||
fferror("Missing close paren");
|
||
} else {
|
||
fieldx++;
|
||
if (token[fieldx])
|
||
fferror("Nothing expected after paren");
|
||
else if (macctrlx < nmacroctrl - 1) {
|
||
macctrlnum[macctrlx] = control_num;
|
||
macctrlparmx[macctrlx] = value;
|
||
macctrldef[macctrlx] = NULL;
|
||
macctrlx++;
|
||
} else fferror("Too many controls");
|
||
}
|
||
}
|
||
} else fferror("Missing paren");
|
||
} else {
|
||
def_type def;
|
||
char symbol[100];
|
||
scansymb(symbol);
|
||
if (fieldx == 1) fferror("Macro name expected");
|
||
else if (token[fieldx] != '(') fferror("Open paren expected");
|
||
else {
|
||
fieldx++;
|
||
def = def_lookup(symbol);
|
||
if (!def) {
|
||
fieldx = 1;
|
||
fferror("Undefined macro");
|
||
} else {
|
||
long val;
|
||
macctrlnum[macctrlx] = 0;
|
||
macctrlparmx[macctrlx] = macctrlnextparm;
|
||
macctrldef[macctrlx] = def;
|
||
while (token[fieldx] != ')' && parseparm(&val)) {
|
||
macctrlparms[macctrlnextparm++] = (short) val;
|
||
macctrlnum[macctrlx]++;
|
||
if (token[fieldx] == ',') {
|
||
fieldx++;
|
||
} else if (token[fieldx] != ')') {
|
||
fferror("Unexpected character");
|
||
break;
|
||
}
|
||
}
|
||
fieldx++;
|
||
macctrlx++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* donextdur
|
||
* Effect: parse a next (N) command
|
||
* Implementation:
|
||
* The syntax is N followed by a duration, so save dur and use dosymdur()
|
||
* to parse the duration field.
|
||
* The form N<digits> is parsed directly with scanint().
|
||
****************************************************************************/
|
||
|
||
private void donextdur()
|
||
{
|
||
ndurp = TRUE; /* flag that N was given */
|
||
if (isdigit(token[fieldx])) {
|
||
ntime = precise(scanint());
|
||
ntime = scale(ntime, (ulong)time_scale, rate);
|
||
if (token[fieldx])
|
||
fferror("Only digits were expected here");
|
||
} else {
|
||
fieldx++;
|
||
ntime = dodur();
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* dopitch
|
||
* Effect: parses a pitch command
|
||
****************************************************************************/
|
||
|
||
private int dopitch()
|
||
{
|
||
int p, octave=0;
|
||
int octflag = FALSE; /* set if octave is specified */
|
||
int oldfieldx = fieldx;
|
||
|
||
p = pitchtable[token[fieldx-1]-'A'];
|
||
while (TRUE) {
|
||
if (token[fieldx] == 'S') { /* sharp */
|
||
p++;
|
||
fieldx++;
|
||
}
|
||
else if (token[fieldx] == 'N') { /* skip */
|
||
fieldx++;
|
||
}
|
||
else if (token[fieldx] == 'F') { /* flat */
|
||
p--;
|
||
fieldx++;
|
||
}
|
||
else if (isdigit(token[fieldx]) && !octflag) { /* octave */
|
||
octave = (int) scanint();
|
||
octflag = TRUE;
|
||
}
|
||
else break; /* none of the above */
|
||
}
|
||
if (octflag) p = (p-48) + 12 * octave; /* adjust p to given octave */
|
||
else { /* adjust p to note nearest the default pitch */
|
||
int octdiff = (p + 126 - pitch) / 12;
|
||
p = p + 120 - (octdiff * 12);
|
||
}
|
||
if (p > maxpitch) { /* pitch in range? */
|
||
int newfield = fieldx;
|
||
fieldx = oldfieldx;
|
||
fferror("Pitch too high");
|
||
fieldx = newfield;
|
||
p = maxpitch;
|
||
}
|
||
/* We really should test for end-of-field, but we don't know if we're
|
||
in a parameter list, so comma may or may not be legal */
|
||
return p;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* doprogram
|
||
* Effect: parses a program change (Z) command
|
||
****************************************************************************/
|
||
|
||
private void doprogram()
|
||
{
|
||
register int program = (int) scanint();
|
||
ctrlflag[PROGRAM_CTRL] = ctrlflag[0] = TRUE;
|
||
if (token[fieldx]) {
|
||
fferror("Z must be followed by digits only");
|
||
} else if (program < minprogram) {
|
||
fieldx = 1;
|
||
fferror("Minimum program of 1 will be used");
|
||
program = minprogram;
|
||
} else if (program > maxprogram) {
|
||
fieldx = 1;
|
||
fferror("Maximum program of 128 will be used");
|
||
program = maxprogram;
|
||
}
|
||
ctrlval[PROGRAM_CTRL] = program - 1;
|
||
}
|
||
|
||
|
||
private void doramp()
|
||
{
|
||
int values[2];
|
||
time_type stepsize = 100L; /* default 10 per second */
|
||
int index = 0;
|
||
ndurp = FALSE;
|
||
values[0] = values[1] = 0;
|
||
while (TRUE) {
|
||
linex += scan();
|
||
fieldx = 1;
|
||
if (nullstring(token)) {
|
||
break;
|
||
} else if (index == 2) { /* must be stepsize in dur syntax */
|
||
stepsize = dodur();
|
||
} else {
|
||
int ctrlx = 0;
|
||
static int ctrl_map[] = { -BEND_CTRL, VOLUME, -TOUCH_CTRL, MODWHEEL };
|
||
|
||
switch (token[0]) {
|
||
case 'M': ctrlx++; /* modwheel */
|
||
case 'O': ctrlx++; /* aftertouch */
|
||
case 'X': ctrlx++; /* volume */
|
||
case 'Y': /* pitch bend */
|
||
|
||
if (index < 2) {
|
||
macctrlnum[index] = ctrl_map[ctrlx];
|
||
macctrlparmx[index] = (int) scanint();
|
||
if (token[fieldx])
|
||
fferror("Only digits expected here");
|
||
macctrldef[index] = NULL;
|
||
} else fferror("Unexpected control");
|
||
break;
|
||
case '~':
|
||
if (index < 2) {
|
||
domacro();
|
||
if (token[fieldx]) fferror("Unexpected character");
|
||
} else fferror("Unexpected control");
|
||
break;
|
||
case 'T':
|
||
if (index < 2) fferror("Control expected");
|
||
dotime();
|
||
break;
|
||
case 'V':
|
||
if (index < 2) fferror("Control expected");
|
||
dovoice();
|
||
break;
|
||
case 'N':
|
||
if (index < 2) fferror("Control expected");
|
||
donextdur();
|
||
break;
|
||
default:
|
||
if (index < 2) fferror("Control expected");
|
||
dur = dodur();
|
||
break;
|
||
}
|
||
if (index == 1 && (macctrlnum[0] != macctrlnum[1] ||
|
||
macctrldef[0] != macctrldef[1])) {
|
||
fferror("Controls do not match");
|
||
}
|
||
}
|
||
index++;
|
||
}
|
||
if (index < 3) fferror("Expected 2 controls and step size");
|
||
else {
|
||
if (macctrldef[0]) {
|
||
int i, j, n;
|
||
n = 0;
|
||
i = macctrlparmx[0];
|
||
j = macctrlparmx[1];
|
||
while (n < macctrlnum[0]) {
|
||
if (macctrlparms[i] != macctrlparms[j]) break;
|
||
n++; i++; j++;
|
||
}
|
||
if (n >= macctrlnum[0]) n = 0;
|
||
/* Note: duration shortened to prevent overlap with next ramp */
|
||
insert_deframp(the_score, seqround(thetime), lineno, voice,
|
||
seqround(stepsize), trunc(dur) - 1, macctrldef[0], macctrlnum[0],
|
||
macctrlparms + macctrlparmx[0], n, macctrlparms[j]);
|
||
} else {
|
||
/* Note: duration shortened to prevent overlap with next ramp */
|
||
insert_ctrlramp(the_score, seqround(thetime), lineno, voice,
|
||
seqround(stepsize), trunc(dur) - 1,
|
||
macctrlnum[0], macctrlparmx[0], macctrlparmx[1]);
|
||
}
|
||
}
|
||
/* advance the time only if an N field was given */
|
||
if (ndurp) thetime += ntime;
|
||
else thetime += dur;
|
||
}
|
||
|
||
|
||
/****************************************************************************
|
||
* dorate
|
||
* Effect: parses a !rate command
|
||
****************************************************************************/
|
||
|
||
private void dorate()
|
||
{
|
||
linex += scan();
|
||
if (!token[0])
|
||
fferror("rate number expected");
|
||
else {
|
||
long oldrate = rate;
|
||
rate = (int) scanint();
|
||
if (token[fieldx])
|
||
fferror("Only digits expected here");
|
||
if (rate == 0) {
|
||
fieldx = 0;
|
||
fferror("Rate 100 will be used here");
|
||
rate = 100L;
|
||
}
|
||
start = thetime;
|
||
/* adjust dur in case it is inherited by next note */
|
||
dur = (dur * oldrate);
|
||
dur = dur / rate;
|
||
}
|
||
}
|
||
|
||
|
||
private void doset(vec_flag)
|
||
boolean vec_flag;
|
||
{
|
||
ndurp = FALSE;
|
||
linex += scan();
|
||
if (!token[0]) fferror("Variable name expected");
|
||
else {
|
||
struct symb_descr *desc = &HASHENTRY(lookup(token));
|
||
if (!desc->symb_type) fferror("Called function not defined");
|
||
else if (vec_flag && (desc->symb_type != vec_symb_type)) {
|
||
fferror("This is not an array");
|
||
} else if (!vec_flag && (desc->symb_type != var_symb_type)) {
|
||
fferror("This is not a variable");
|
||
} else {
|
||
int numargs = 1 + vec_flag;
|
||
int value[2];
|
||
int i;
|
||
int *address = desc->ptr.intptr;
|
||
value[0] = value[1] = 0;
|
||
i = 0;
|
||
while (TRUE) {
|
||
linex += scan();
|
||
if (nullstring(token)) {
|
||
break;
|
||
} else if (isdigit(token[0]) || token[0] == '-' ||
|
||
token[0] == '+') {
|
||
if (i < numargs) {
|
||
value[i++] = (int) scansgnint();
|
||
if (token[fieldx])
|
||
fferror("Only digits expected here");
|
||
} else fferror(too_many_error);
|
||
} else {
|
||
switch (token[0]) {
|
||
case 'T':
|
||
fieldx = 1;
|
||
dotime();
|
||
break;
|
||
case 'V':
|
||
fieldx = 1;
|
||
dovoice();
|
||
break;
|
||
case 'N':
|
||
fieldx = 1;
|
||
donextdur();
|
||
break;
|
||
default:
|
||
fieldx++;
|
||
if (i < numargs) {
|
||
value[i++] = seqround(dodur());
|
||
} else fferror(too_many_error);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (vec_flag && i != 2) fferror("No index given");
|
||
if (vec_flag) {
|
||
if (value[0] >= desc->size) {
|
||
fferror("Subscript out of bounds");
|
||
return;
|
||
}
|
||
/* reduce to the seti case: */
|
||
address += value[0]; /* compute the vector address */
|
||
value[0] = value[1]; /* set up value[] and i as if */
|
||
i--; /* this were seti, not setv */
|
||
}
|
||
if (i != 1) fferror("No value given");
|
||
insert_seti(the_score, seqround(thetime), lineno, voice,
|
||
address, value[0]);
|
||
/* advance the time only if an N field was given */
|
||
if (ndurp) thetime += ntime;
|
||
}
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* dospecial
|
||
* Effect: parses special (those starting with "!") commands
|
||
****************************************************************************/
|
||
|
||
private void dospecial()
|
||
{
|
||
switch (issymbol()) {
|
||
case sym_tempo:
|
||
dotempo();
|
||
break;
|
||
case sym_rate:
|
||
dorate();
|
||
break;
|
||
case sym_csec:
|
||
/* adjust dur for inheritance by next note */
|
||
dur = (dur * 1000L) / time_scale;
|
||
time_scale = 1000L;
|
||
break;
|
||
case sym_msec:
|
||
dur = (dur * 100L) / time_scale;
|
||
time_scale = 100L;
|
||
break;
|
||
case sym_seti:
|
||
doset(FALSE);
|
||
break;
|
||
case sym_setv:
|
||
doset(TRUE);
|
||
break;
|
||
case sym_call:
|
||
docall();
|
||
break;
|
||
case sym_ramp:
|
||
doramp();
|
||
break;
|
||
case sym_clock:
|
||
doclock();
|
||
break;
|
||
case sym_def:
|
||
dodef();
|
||
break;
|
||
case sym_end:
|
||
end_flag = TRUE;
|
||
break;
|
||
default:
|
||
fferror("Special command expected");
|
||
}
|
||
parseend(); /* flush the rest of the line */
|
||
}
|
||
|
||
/****************************************************************************
|
||
* dosymdur
|
||
* Effect: parses a duration (^, %, S, I, Q, H, or W) command
|
||
****************************************************************************/
|
||
|
||
private time_type dosymdur()
|
||
{
|
||
int i, dotcnt = 0;
|
||
long dotfactor;
|
||
time_type result = 0;
|
||
|
||
for (i = 0; i < durtable_len; i++) {
|
||
if (durtable[i].symbol == token[fieldx-1]) {
|
||
/* the shift right is because durs are stored doubled because
|
||
* otherwise a 64th note would have the value 75/2: */
|
||
result = precise(durtable[i].value) >> 1;
|
||
break;
|
||
}
|
||
}
|
||
if (i == durtable_len) {
|
||
fieldx--;
|
||
fferror("Duration expected: one of W, H, Q, I, S, %, or ^");
|
||
return 0L;
|
||
}
|
||
while (token[fieldx]) {
|
||
if (token[fieldx] == 'T') { /* triplet notation */
|
||
result = (result * 2) / 3; /* lose a bit but avoid scale() */
|
||
fieldx++;
|
||
}
|
||
else if (token[fieldx] == '.') { /* dotted notation */
|
||
dotcnt++;
|
||
fieldx++;
|
||
}
|
||
else if (token[fieldx] == '/') {
|
||
long divisor;
|
||
fieldx++;
|
||
divisor = scanint();
|
||
if (divisor > 0) result = result / divisor;
|
||
else fferror("non-zero integer expected");
|
||
}
|
||
else if (isdigit(token[fieldx])) { /* numbers are multipliers */
|
||
result = result * scanint();
|
||
}
|
||
else break;
|
||
}
|
||
dotfactor = 1L;
|
||
for (i=1; i<=dotcnt; i++)
|
||
dotfactor = dotfactor * 2;
|
||
result = (2 * result) - (result / dotfactor);
|
||
|
||
return scale(result, 100L, tempo); /* time in milliseconds */
|
||
}
|
||
|
||
/****************************************************************************
|
||
* dotempo
|
||
* Effect: parses a !tempo command
|
||
****************************************************************************/
|
||
|
||
private void dotempo()
|
||
{
|
||
linex += scan();
|
||
if (!token[0])
|
||
fferror("Tempo number expected");
|
||
else {
|
||
long oldtempo = tempo;
|
||
tempo = scanint();
|
||
if (token[fieldx])
|
||
fferror("Only digits expected here");
|
||
if (tempo == 0) {
|
||
fieldx = 0;
|
||
fferror("Tempo 100 will be used here");
|
||
tempo = 100L;
|
||
}
|
||
start = thetime;
|
||
/* adjust dur in case it is inherited by next note */
|
||
if (symbolic_dur_flag) {
|
||
dur = (dur * oldtempo);
|
||
dur = dur / tempo;
|
||
}
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* dotime
|
||
* Effect: parses a time (T) command
|
||
* Implementation: see implementation of donextdur()
|
||
****************************************************************************/
|
||
|
||
private void dotime()
|
||
{
|
||
if (isdigit(token[fieldx])) {
|
||
thetime = precise(scanint());
|
||
thetime = scale(thetime, (ulong)time_scale, rate);
|
||
if (token[fieldx] )
|
||
fferror("Only digits were expected here");
|
||
} else {
|
||
fieldx++;
|
||
thetime = dodur();
|
||
}
|
||
thetime += start; /* time is relative to start */
|
||
}
|
||
|
||
/****************************************************************************
|
||
* dovoice
|
||
* Effect: parse a voice (V) command (the voice is the MIDI channel)
|
||
****************************************************************************/
|
||
|
||
private void dovoice()
|
||
{
|
||
if (isdigit(token[fieldx])) {
|
||
voice = (int) scanint();
|
||
if (token[fieldx])
|
||
fferror("V must be followed by digits only");
|
||
if (voice > MAX_CHANNELS) {
|
||
char msg[40];
|
||
sprintf(msg, "number too high, using %d instead", MAX_CHANNELS);
|
||
fferror(msg);
|
||
voice = MAX_CHANNELS;
|
||
}
|
||
else if (voice < 1) {
|
||
fferror("number too low, using 1 instead");
|
||
voice = 1;
|
||
}
|
||
}
|
||
else fferror("No digit after V");
|
||
}
|
||
|
||
/****************************************************************************
|
||
* fferror
|
||
* Inputs:
|
||
* char *s: an error message string
|
||
* Effect:
|
||
* prints the line with the error
|
||
* puts a cursor (^) at the error location
|
||
* prints the error message (s)
|
||
* Implementation:
|
||
* this routine prints a carat under the character that
|
||
* was copied into token[fieldx]. E.g. if fieldx = 0, the
|
||
* carat will point to the first character in the field.
|
||
****************************************************************************/
|
||
|
||
private void fferror(s)
|
||
char *s;
|
||
{
|
||
gprintf(ERROR, "%3d | %s", lineno, line);
|
||
marker(linex-strlen(token)+fieldx+1+6);
|
||
gprintf(ERROR, "Error: %s.\n", s);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* init
|
||
* Effect:
|
||
* initializes the state variables
|
||
****************************************************************************/
|
||
|
||
private void init()
|
||
{
|
||
int i;
|
||
|
||
end_flag = FALSE;
|
||
|
||
/* initial (default) values for all state variables */
|
||
symbolic_dur_flag = TRUE; /* default dur is symbolic */
|
||
for (i = 0; i < nctrl; i++) {
|
||
/* no initial control changes */
|
||
ctrlflag[i] = FALSE;
|
||
ctrlval[i] = 0;
|
||
}
|
||
|
||
lineno = 0;
|
||
pitch = seq_dflt_pitch;
|
||
loud = seq_dflt_loud;
|
||
voice = seq_dflt_voice;
|
||
time_scale = 1000L;
|
||
tempo = 100L;
|
||
rate = 100L;
|
||
dur = precise(600); /* default dur is quarter note */
|
||
thetime = precise(0);
|
||
start = thetime;
|
||
ntime = 0L;
|
||
ticksize = 0L;
|
||
artic = 100;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* ins_a_note
|
||
* Returns:
|
||
* boolean: TRUE on success, FALSE if not enough memory
|
||
* Effect:
|
||
* note events (if any) corresponding to the current line are inserted
|
||
* Implementation:
|
||
* if a note on should occur after a note off and doesn't, and the
|
||
* two notes have the same pitch, then the note off can cancel the
|
||
* note on. to make it unlikely that roundoff will cause this situation,
|
||
* dur is decreased by one half of a clock tick before rounding.
|
||
* also, phase2 gives precedence to note-offs that are simultaneous
|
||
* with note-ons.
|
||
****************************************************************************/
|
||
|
||
private boolean ins_a_note()
|
||
{
|
||
long the_dur = (trunc(dur) * artic + 50) / 100;
|
||
int the_pitch = pitch;
|
||
event_type note;
|
||
if (rest_flag) the_pitch = NO_PITCH;
|
||
note = insert_note(the_score, seqround(thetime), lineno, voice,
|
||
the_pitch, the_dur, loud);
|
||
if (!note) return FALSE;
|
||
return TRUE; /* success! */
|
||
}
|
||
|
||
/****************************************************************************
|
||
* ins_ctrls
|
||
* Returns:
|
||
* boolean: TRUE on success, FALSE if not enough memory
|
||
* Effect:
|
||
* control events corresponding to current line are inserted in score
|
||
* Implementation:
|
||
* ctrlflag[i] is TRUE if control i was specified in this line, so
|
||
* insert one control change for each ctrlflag[i] that is TRUE
|
||
****************************************************************************/
|
||
|
||
private boolean ins_ctrls()
|
||
{
|
||
int i;
|
||
event_type ctrl;
|
||
|
||
for (i = 1; i < nctrl; i++) {
|
||
if (ctrlflag[i]) {
|
||
ctrl = insert_ctrl(the_score, seqround(thetime), lineno, i, voice,
|
||
ctrlval[i]);
|
||
if (!ctrl) return FALSE;
|
||
ctrlflag[i] = FALSE;
|
||
ctrlval[i] = 0;
|
||
}
|
||
}
|
||
return TRUE; /* success! */
|
||
}
|
||
|
||
/****************************************************************************
|
||
* issymbol
|
||
* Outputs: returns symbol number, or -1 if no match
|
||
* Assumes: token[1] has the symbol to look up (token[0] == '!')
|
||
****************************************************************************/
|
||
|
||
private int issymbol()
|
||
{
|
||
int i, symb_num;
|
||
char *sym;
|
||
|
||
for (symb_num = 0; symb_num < sym_n; symb_num++) {
|
||
sym = ssymbols[symb_num];
|
||
i = 1;
|
||
while (TRUE) {
|
||
if (token[i] != *sym) break;
|
||
if (*sym == 0) return symb_num;
|
||
sym++;
|
||
i++;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* marker
|
||
* Inputs:
|
||
* int count: the number of characters to indent
|
||
* Effect:
|
||
* prints a carat (^) at the position specified on file stderr
|
||
****************************************************************************/
|
||
|
||
private void marker(count)
|
||
int count;
|
||
{
|
||
int i;
|
||
char s[128];
|
||
for (i=0; i<(count-1); s[i++]=' ') /* */ ;
|
||
s[count-1] = '^';
|
||
s[count] = '\0';
|
||
gprintf(ERROR,"%s\n",s);
|
||
}
|
||
|
||
/*****************************************************************
|
||
* parseend
|
||
* Effect:
|
||
* parse the note terminator, either ",", ";", EOS or "\n"
|
||
*
|
||
****************************************************************/
|
||
|
||
private void parseend()
|
||
{
|
||
boolean done = FALSE;
|
||
while (!done) {
|
||
linex += scan1(&line[linex]);
|
||
switch (token[0]) {
|
||
case ',':
|
||
ndurp = TRUE; /* switch that next time was specified */
|
||
ntime = 0L;
|
||
done = TRUE;
|
||
break;
|
||
case ';':
|
||
case '\n':
|
||
case EOS:
|
||
done = TRUE;
|
||
break;
|
||
case ' ':
|
||
case '\t':
|
||
break; /* skip over blanks and scan1 again */
|
||
default:
|
||
fferror("Unexpected token");
|
||
linex += scan(); /* flush the token */
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* parsefield
|
||
* Effect: looks at first character of token and calls a parsing routine
|
||
*
|
||
****************************************************************************/
|
||
|
||
private void parsefield()
|
||
{
|
||
fieldx = 1;
|
||
switch (token[0]) {
|
||
case 'T' :
|
||
dotime();
|
||
break;
|
||
case 'U':
|
||
case 'W':
|
||
case 'H':
|
||
case 'Q':
|
||
case 'S':
|
||
case 'I':
|
||
case '%':
|
||
case '^':
|
||
dur = dodur();
|
||
break;
|
||
case 'R':
|
||
do_a_rest();
|
||
break;
|
||
case 'A':
|
||
case 'B':
|
||
case 'C':
|
||
case 'D':
|
||
case 'E':
|
||
case 'F':
|
||
case 'G':
|
||
pitch = dopitch();
|
||
pitch_flag = TRUE;
|
||
break;
|
||
case 'P':
|
||
pitch = doabspitch();
|
||
pitch_flag = TRUE;
|
||
break;
|
||
case 'L':
|
||
loud = doloud();
|
||
break;
|
||
case 'N':
|
||
donextdur();
|
||
break;
|
||
/* case 'J':
|
||
* doctrl(1);
|
||
* break;
|
||
*/
|
||
case 'K':
|
||
doctrl(PSWITCH_CTRL);
|
||
break;
|
||
case 'M':
|
||
doctrl(MODWHEEL_CTRL);
|
||
break;
|
||
case 'O':
|
||
doctrl(TOUCH_CTRL);
|
||
break;
|
||
case 'X':
|
||
doctrl(VOLUME_CTRL);
|
||
break;
|
||
case 'Y':
|
||
doctrl(BEND_CTRL);
|
||
break;
|
||
case 'Z':
|
||
doprogram();
|
||
break;
|
||
case 'V':
|
||
dovoice();
|
||
break;
|
||
case '~':
|
||
domacro();
|
||
break;
|
||
case '*':
|
||
docomment();
|
||
break;
|
||
case '#':
|
||
doartic();
|
||
break;
|
||
default :
|
||
doerror();
|
||
break;
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* parsenote
|
||
* Effect:
|
||
* parses a note line -- control events (if any) and note event (if
|
||
* present) are inserted into score
|
||
* Assumes:
|
||
* line contains a string to be parsed
|
||
****************************************************************************/
|
||
|
||
private boolean parsenote()
|
||
{
|
||
boolean out_of_memory = FALSE;
|
||
int i;
|
||
|
||
ndurp = FALSE;
|
||
rest_flag = FALSE;
|
||
|
||
/* this loop reads tokens for a note */
|
||
while (token[0]) {
|
||
parsefield();
|
||
linex += scan();
|
||
}
|
||
|
||
parseend(); /* take care of note terminator */
|
||
|
||
/*
|
||
* insert ctrl's first so that will come before the note.
|
||
*/
|
||
if (ctrlflag[0]) {
|
||
out_of_memory |= !ins_ctrls();
|
||
/* don't reset ctrlflag[0], it's used below */
|
||
}
|
||
|
||
/*
|
||
* insert macro's
|
||
*/
|
||
for (i = 0; i < macctrlx; i++) {
|
||
event_type ctrl;
|
||
if (macctrldef[i] == NULL) {
|
||
ctrl = insert_macctrl(the_score, seqround(thetime), lineno,
|
||
macctrlnum[i], voice, macctrlparmx[i]);
|
||
} else {
|
||
ctrl = insert_macro(the_score, seqround(thetime), lineno,
|
||
macctrldef[i], voice, macctrlnum[i],
|
||
&(macctrlparms[macctrlparmx[i]]));
|
||
}
|
||
out_of_memory |= (ctrl == NULL);
|
||
}
|
||
|
||
/* insert a note if
|
||
* (1) a pitch was specified OR
|
||
* (2) no control was specified and this is not a rest
|
||
* (it's a pitch by default)
|
||
*
|
||
* NOTE: program changes during rests are advised since
|
||
* synthesizers may not be able to process a program
|
||
* change followed immediately by a note-on. In fact, this
|
||
* is why we insert notes whose pitch is NO_PITCH -- so that
|
||
* the program change can be processed during the rest.
|
||
*/
|
||
if (pitch_flag ||
|
||
(!ctrlflag[0] && !rest_flag && (macctrlx == 0))) {
|
||
out_of_memory |= !ins_a_note();
|
||
}
|
||
|
||
if (ndurp) thetime += ntime;
|
||
else thetime += dur;
|
||
|
||
return out_of_memory;
|
||
}
|
||
|
||
|
||
private boolean parseparm(valptr)
|
||
long *valptr;
|
||
{
|
||
register char c = token[fieldx];
|
||
if (isdigit(c) || c == '-') {
|
||
*valptr = scansgnint();
|
||
return TRUE;
|
||
} else {
|
||
switch (c) {
|
||
case 'P':
|
||
fieldx++;
|
||
*valptr = doabspitch();
|
||
return TRUE;
|
||
case 'A':
|
||
case 'B':
|
||
case 'C':
|
||
case 'D':
|
||
case 'E':
|
||
case 'F':
|
||
case 'G':
|
||
fieldx++;
|
||
*valptr = dopitch();
|
||
return TRUE;
|
||
case 'U':
|
||
case 'W':
|
||
case 'H':
|
||
case 'Q':
|
||
case 'I':
|
||
case 'S':
|
||
case '%':
|
||
case '^':
|
||
fieldx++;
|
||
*valptr = seqround(dodur());
|
||
return TRUE;
|
||
case 'L':
|
||
fieldx++;
|
||
*valptr = doloud();
|
||
return TRUE;
|
||
case '\'':
|
||
fieldx++;
|
||
*valptr = token[fieldx];
|
||
fieldx++;
|
||
if (token[fieldx] != '\'') {
|
||
fferror("single quote expected");
|
||
}
|
||
fieldx++;
|
||
return TRUE;
|
||
default:
|
||
fferror("Parameter expected");
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/****************************************************************************
|
||
* scale
|
||
* Inputs:
|
||
* time_type x
|
||
* int (ulong?) n, d
|
||
* Outputs:
|
||
* returns time_type: result of scaling x by n/d
|
||
****************************************************************************/
|
||
|
||
public time_type scale(x, n, d)
|
||
ulong x;
|
||
ulong n, d;
|
||
{
|
||
ulong lo = (x & 0xFFFFL) * n;
|
||
ulong hi = (x >> 16) * n;
|
||
ulong res = hi / d;
|
||
lo = (((hi - (res * d)) << 16) + lo + (d >> 1)) / d;
|
||
return (time_type)( (res << 16) + lo );
|
||
}
|
||
|
||
/****************************************************************************
|
||
* scan
|
||
* Inputs:
|
||
* char *start: the string to scan
|
||
* Outputs:
|
||
* returns int: the index of the next char in start to scan
|
||
* Effect:
|
||
* skips over leading blanks
|
||
* copies characters from start into token, converting to upper case
|
||
* scanning stops on delimiter: one of space, tab, newline, semicolon
|
||
****************************************************************************/
|
||
|
||
private int scan()
|
||
{
|
||
char *start = line + linex;
|
||
register char c;
|
||
register int i = 0;
|
||
register int j = 0;
|
||
register int parens = 0;
|
||
|
||
while (((c = start[i]) == ' ') || (c == '\t')) i++;
|
||
|
||
while ((c = start[i]) != ' ' && c != '\n' && c != '\t' && c != EOS &&
|
||
(c != ',' || token[0] == '~' || parens > 0) && c != ';') {
|
||
|
||
if (islower(start[i])) token[j] = toupper(start[i]);
|
||
else token[j] = start[i];
|
||
if (c == '(') parens++;
|
||
else if (c == ')') parens--;
|
||
j++;
|
||
i++;
|
||
}
|
||
token[j] = '\0';
|
||
|
||
fieldx = 0;
|
||
if (parens) fferror("Unbalanced parens");
|
||
|
||
return i;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* scan1
|
||
* Inputs:
|
||
* char *start: the string to scan
|
||
* Outputs:
|
||
* returns int: the index of the next char in start to scan
|
||
* Effect:
|
||
* copies one char from start into token, converting to upper case
|
||
****************************************************************************/
|
||
|
||
private int scan1(start)
|
||
char *start;
|
||
{
|
||
int i = 0;
|
||
|
||
token[0] = *start;
|
||
if (islower(token[0])) token[0] = toupper(token[0]);
|
||
|
||
if (!nullstring(token)) {
|
||
token[1] = '\0';
|
||
i = 1;
|
||
}
|
||
fieldx = 0;
|
||
return i;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* scanint
|
||
* Outputs:
|
||
* returns long: the scanned integer
|
||
* Effect:
|
||
* scans an unsigned long from token, starting at fieldx
|
||
* fieldx is incremented to end of the integer
|
||
****************************************************************************/
|
||
|
||
private long scanint()
|
||
{
|
||
long i = 0;
|
||
char c;
|
||
while ((c = token[fieldx])) {
|
||
if (isdigit(c)) {
|
||
i = (i*10) + (c - '0');
|
||
fieldx++;
|
||
} else return i;
|
||
}
|
||
return i;
|
||
}
|
||
|
||
private long scansgnint()
|
||
{
|
||
if (token[fieldx] == '-') {
|
||
fieldx++;
|
||
return -scanint();
|
||
} else {
|
||
if (token[fieldx] == '+') {
|
||
fieldx++;
|
||
}
|
||
return scanint();
|
||
}
|
||
}
|
||
|
||
|
||
/* scansymb -- scan a symbol from the token */
|
||
/**/
|
||
private void scansymb(str)
|
||
char *str;
|
||
{
|
||
char c;
|
||
while ((c = token[fieldx])) {
|
||
if (isdigit(c) || isalpha(c) || c == '_') {
|
||
*str++ = c;
|
||
fieldx++;
|
||
} else break;
|
||
}
|
||
*str = EOS;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* seq_read
|
||
* Inputs:
|
||
* seq_type seq: the sequence to receive the score
|
||
* FILE *fp: input file
|
||
* Outputs:
|
||
* none
|
||
* Effect:
|
||
* parses score from input file and builds score data structure
|
||
****************************************************************************/
|
||
|
||
void seq_read(seq, fp)
|
||
seq_type seq;
|
||
FILE *fp;
|
||
{
|
||
boolean out_of_memory = FALSE; /* set when no more memory */
|
||
/* printf("seq_read: chunklist is 0x%x\n", seq->chunklist); */
|
||
the_score = seq; /* current sequence is a global within this module */
|
||
if (!seq) return;
|
||
init();
|
||
lineno = 0;
|
||
|
||
/* make sure line is well terminated or scan might run off the end */
|
||
line[linesize - 1] = EOS;
|
||
line[linesize - 2] = '\n';
|
||
|
||
/* this loop reads lines */
|
||
while ((fgets(line, linesize - 2, fp) != NULL) && !out_of_memory &&
|
||
!check_aborted() && !end_flag) {
|
||
lineno++;
|
||
linex = 0;
|
||
/* this loop reads notes from a line */
|
||
while ((line[linex] != EOS) && !out_of_memory) {
|
||
/* loop invariant: line[linex] is first char of next note */
|
||
ctrlflag[0] = FALSE; /* other ctrlflags are reset by ins_ctrls() */
|
||
macctrlx = 0;
|
||
macctrlnextparm = 0;
|
||
pitch_flag = FALSE;
|
||
linex += scan();
|
||
if (!nullstring(token)) {
|
||
if (token[0] == '*') docomment();
|
||
else if (token[0] == '!') dospecial();
|
||
else out_of_memory = parsenote();
|
||
}
|
||
else parseend();
|
||
}
|
||
}
|
||
|
||
if (out_of_memory) {
|
||
gprintf(ERROR, "Out of note memory at line %d,\n", lineno-1);
|
||
gprintf(ERROR, " the rest of your file will be ignored.\n");
|
||
}
|
||
|
||
if (check_aborted()) {
|
||
gprintf(ERROR, "User aborted score input,\n");
|
||
gprintf(ERROR, " the rest of your file will be ignored.\n");
|
||
if (abort_flag == BREAK_LEVEL) abort_flag = 0;
|
||
}
|
||
|
||
/* fclose(fp); -- don't close the file; if you do, Nyquist's garbage
|
||
collector will close Nyquist's copy, and closing the file twice
|
||
in Linux will crash Nyquist */
|
||
|
||
gprintf(TRANS, "\nLoaded Adagio file with %ld note(s), %ld ctrl(s).\n\n",
|
||
seq_notecount(the_score), seq_ctrlcount(the_score));
|
||
}
|