diff --git a/Release.zip b/Release.zip new file mode 100644 index 0000000000000000000000000000000000000000..e860ec6b4e1d435368cd6eaa79fd7fd242153c0f Binary files /dev/null and b/Release.zip differ diff --git a/Release/EffectsMenuDefaults.xml b/Release/EffectsMenuDefaults.xml deleted file mode 100644 index a6d55e94ce88704bea60dd5061da791559c72779..0000000000000000000000000000000000000000 --- a/Release/EffectsMenuDefaults.xml +++ /dev/null @@ -1,98 +0,0 @@ -<EffectMenuList> - <Group> - <!-- Effects menu group name; audio dynamics compression, not data compression --> - <Name>Volume and Compression</Name> - <Effects> - <Effect>Amplify</Effect> - <Effect>Compressor</Effect> - <Effect>Limiter</Effect> - <Effect>Normalize</Effect> - <Effect>Loudness Normalization</Effect> - <Effect>Auto Duck</Effect> - </Effects> - </Group> - <Group> - <!-- Effects menu group name --> - <Name>Fading</Name> - <Effects> - <Effect>Fade In</Effect> - <Effect>Fade Out</Effect> - <Effect>Studio Fade Out</Effect> - <Effect>Adjustable Fade</Effect> - <Effect>Crossfade Clips</Effect> - <Effect>Crossfade Tracks</Effect> - </Effects> - </Group> - <Group> - <Name>Pitch and Tempo</Name> - <Effects> - <Effect>Change Pitch</Effect> - <Effect>Change Speed and Pitch</Effect> - <Effect>Change Tempo</Effect> - <Effect>Paulstretch</Effect> - <Effect>Sliding Stretch</Effect> - </Effects> - </Group> - <Group> - <Name>EQ and Filters</Name> - <Effects> - <Effect>Bass and Treble</Effect> - <Effect>Graphic EQ</Effect> - <Effect>Filter Curve EQ</Effect> - <Effect>High-Pass Filter</Effect> - <Effect>Low-Pass Filter</Effect> - <Effect>Shelf Filter</Effect> - <Effect>Notch Filter</Effect> - <Effect>Classic Filters</Effect> - </Effects> - </Group> - <Group> - <Name>Noise Removal and Repair</Name> - <Effects> - <Effect>Click Removal</Effect> - <Effect>Noise Reduction</Effect> - <Effect>Noise Gate</Effect> - <Effect>Repair</Effect> - <Effect>Clip Fix</Effect> - </Effects> - </Group> - <Group> - <Name>Delay and Reverb</Name> - <Effects> - <Effect>Echo</Effect> - <Effect>Reverb</Effect> - <Effect>Delay</Effect> - </Effects> - </Group> - <Group> - <Name>Distortion and Modulation</Name> - <Effects> - <Effect>Tremolo</Effect> - <Effect>Distortion</Effect> - <Effect>Wahwah</Effect> - <Effect>Phaser</Effect> - <Effect>Vocoder</Effect> - </Effects> - </Group> - <Group> - <!-- Effects menu group name --> - <Name>Special</Name> - <Effects> - <Effect>Repeat</Effect> - <Effect>Reverse</Effect> - <Effect>Invert</Effect> - <Effect>Truncate Silence</Effect> - <Effect>Vocal Reduction and Isolation</Effect> - <Effect>Vocal Remover</Effect> - </Effects> - </Group> - <Group> - <Name>Spectral Tools</Name> - <Effects> - <Effect>Spectral Delete</Effect> - <Effect>Spectral Edit Multi Tool</Effect> - <Effect>Spectral Edit Parametric EQ</Effect> - <Effect>Spectral Edit Shelves</Effect> - </Effects> - </Group> -</EffectMenuList> diff --git a/Release/Languages/af/audacity.mo b/Release/Languages/af/audacity.mo deleted file mode 100644 index 970a3108a5d3c06060accf665d4c44bece03cfa4..0000000000000000000000000000000000000000 Binary files a/Release/Languages/af/audacity.mo and /dev/null differ diff --git a/Release/Languages/ar/audacity.mo b/Release/Languages/ar/audacity.mo deleted file mode 100644 index 145839f308bab09a821b4e99e7c6bad77f749f77..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ar/audacity.mo and /dev/null differ diff --git a/Release/Languages/be/audacity.mo b/Release/Languages/be/audacity.mo deleted file mode 100644 index 1bdba3c3c531919df9817fb767f89704f70cb179..0000000000000000000000000000000000000000 Binary files a/Release/Languages/be/audacity.mo and /dev/null differ diff --git a/Release/Languages/bg/audacity.mo b/Release/Languages/bg/audacity.mo deleted file mode 100644 index c75306e8041b0038954068efafd17061dab8b193..0000000000000000000000000000000000000000 Binary files a/Release/Languages/bg/audacity.mo and /dev/null differ diff --git a/Release/Languages/bn/audacity.mo b/Release/Languages/bn/audacity.mo deleted file mode 100644 index 10302253e6120235c115e80167e71eb6c51c6d33..0000000000000000000000000000000000000000 Binary files a/Release/Languages/bn/audacity.mo and /dev/null differ diff --git a/Release/Languages/bs/audacity.mo b/Release/Languages/bs/audacity.mo deleted file mode 100644 index d160e5bc7992afb503d05a2b043023a0a90c704e..0000000000000000000000000000000000000000 Binary files a/Release/Languages/bs/audacity.mo and /dev/null differ diff --git a/Release/Languages/ca/audacity.mo b/Release/Languages/ca/audacity.mo deleted file mode 100644 index fd622b2409055711355f5eddf84a474e402a31b4..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ca/audacity.mo and /dev/null differ diff --git a/Release/Languages/ca_ES@valencia/audacity.mo b/Release/Languages/ca_ES@valencia/audacity.mo deleted file mode 100644 index d92359b54c6cb1e972ec36ec98010fa9b7dd60e8..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ca_ES@valencia/audacity.mo and /dev/null differ diff --git a/Release/Languages/co/audacity.mo b/Release/Languages/co/audacity.mo deleted file mode 100644 index bb7e30a6a55c6fb4d2026d814b7f140385109069..0000000000000000000000000000000000000000 Binary files a/Release/Languages/co/audacity.mo and /dev/null differ diff --git a/Release/Languages/cs/audacity.mo b/Release/Languages/cs/audacity.mo deleted file mode 100644 index 05f3b60ae4abf0665b9219406f78484a3d5ce9d4..0000000000000000000000000000000000000000 Binary files a/Release/Languages/cs/audacity.mo and /dev/null differ diff --git a/Release/Languages/cy/audacity.mo b/Release/Languages/cy/audacity.mo deleted file mode 100644 index d0ad28dd94039c1f4d176cabc939c5e5355f2154..0000000000000000000000000000000000000000 Binary files a/Release/Languages/cy/audacity.mo and /dev/null differ diff --git a/Release/Languages/da/audacity.mo b/Release/Languages/da/audacity.mo deleted file mode 100644 index 3a88d48622acd6df000b0bc5564c83e535bba131..0000000000000000000000000000000000000000 Binary files a/Release/Languages/da/audacity.mo and /dev/null differ diff --git a/Release/Languages/de/audacity.mo b/Release/Languages/de/audacity.mo deleted file mode 100644 index e2bf17fd1fe76bf33205afb32bbad28451861935..0000000000000000000000000000000000000000 Binary files a/Release/Languages/de/audacity.mo and /dev/null differ diff --git a/Release/Languages/el/audacity.mo b/Release/Languages/el/audacity.mo deleted file mode 100644 index b5417eefacc193dfb17025964d9e865068309c15..0000000000000000000000000000000000000000 Binary files a/Release/Languages/el/audacity.mo and /dev/null differ diff --git a/Release/Languages/es/audacity.mo b/Release/Languages/es/audacity.mo deleted file mode 100644 index 0b62b6b64e31c1a8050acb06f9afd117dfd6b206..0000000000000000000000000000000000000000 Binary files a/Release/Languages/es/audacity.mo and /dev/null differ diff --git a/Release/Languages/eu/audacity.mo b/Release/Languages/eu/audacity.mo deleted file mode 100644 index 40d5ae1d88b3c0ebfb376ceedb56c18a68ef9871..0000000000000000000000000000000000000000 Binary files a/Release/Languages/eu/audacity.mo and /dev/null differ diff --git a/Release/Languages/eu_ES/audacity.mo b/Release/Languages/eu_ES/audacity.mo deleted file mode 100644 index 473499ac3348f6e7c1e71e6dfd470845a8231782..0000000000000000000000000000000000000000 Binary files a/Release/Languages/eu_ES/audacity.mo and /dev/null differ diff --git a/Release/Languages/fa/audacity.mo b/Release/Languages/fa/audacity.mo deleted file mode 100644 index 4bcabb9d5026e8e341c9c7b2f9bbac834a1c3736..0000000000000000000000000000000000000000 Binary files a/Release/Languages/fa/audacity.mo and /dev/null differ diff --git a/Release/Languages/fi/audacity.mo b/Release/Languages/fi/audacity.mo deleted file mode 100644 index b3b2eee00e682f941a53a282f439facc6740208f..0000000000000000000000000000000000000000 Binary files a/Release/Languages/fi/audacity.mo and /dev/null differ diff --git a/Release/Languages/fr/audacity.mo b/Release/Languages/fr/audacity.mo deleted file mode 100644 index 8621984b56ce0ef75a4b10674aa88c0ed77b539b..0000000000000000000000000000000000000000 Binary files a/Release/Languages/fr/audacity.mo and /dev/null differ diff --git a/Release/Languages/ga/audacity.mo b/Release/Languages/ga/audacity.mo deleted file mode 100644 index 6c18dbd40550ccdd8011e66679a96ba76c2bf4fc..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ga/audacity.mo and /dev/null differ diff --git a/Release/Languages/gl/audacity.mo b/Release/Languages/gl/audacity.mo deleted file mode 100644 index 0fbf0e0655d66532742295688eb1e5093da37431..0000000000000000000000000000000000000000 Binary files a/Release/Languages/gl/audacity.mo and /dev/null differ diff --git a/Release/Languages/he/audacity.mo b/Release/Languages/he/audacity.mo deleted file mode 100644 index a5bf9570a24ac12d7ec4f0fbe0cf95bbd900474b..0000000000000000000000000000000000000000 Binary files a/Release/Languages/he/audacity.mo and /dev/null differ diff --git a/Release/Languages/hi/audacity.mo b/Release/Languages/hi/audacity.mo deleted file mode 100644 index f3b2e08e3ba1ceac7dffeec06e77bce5bca6ae54..0000000000000000000000000000000000000000 Binary files a/Release/Languages/hi/audacity.mo and /dev/null differ diff --git a/Release/Languages/hr/audacity.mo b/Release/Languages/hr/audacity.mo deleted file mode 100644 index 316678e61d8d8371c372141f9d6f2d3e72e3efcd..0000000000000000000000000000000000000000 Binary files a/Release/Languages/hr/audacity.mo and /dev/null differ diff --git a/Release/Languages/hu/audacity.mo b/Release/Languages/hu/audacity.mo deleted file mode 100644 index 005db52746e3cbfc75aa690de671b864ecd0e8fa..0000000000000000000000000000000000000000 Binary files a/Release/Languages/hu/audacity.mo and /dev/null differ diff --git a/Release/Languages/hy/audacity.mo b/Release/Languages/hy/audacity.mo deleted file mode 100644 index fe91b5386d5db6e80d66af432a93681a2b30c54e..0000000000000000000000000000000000000000 Binary files a/Release/Languages/hy/audacity.mo and /dev/null differ diff --git a/Release/Languages/id/audacity.mo b/Release/Languages/id/audacity.mo deleted file mode 100644 index 49daf2b5ac1f12a6044ad580efc8ae91c1944b4f..0000000000000000000000000000000000000000 Binary files a/Release/Languages/id/audacity.mo and /dev/null differ diff --git a/Release/Languages/it/audacity.mo b/Release/Languages/it/audacity.mo deleted file mode 100644 index 4a53e043bf5b363f9cfe991cd47b8ce544617496..0000000000000000000000000000000000000000 Binary files a/Release/Languages/it/audacity.mo and /dev/null differ diff --git a/Release/Languages/ja/audacity.mo b/Release/Languages/ja/audacity.mo deleted file mode 100644 index c7d338e96a93e35482ac9d3368e072173f4b6ecc..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ja/audacity.mo and /dev/null differ diff --git a/Release/Languages/ka/audacity.mo b/Release/Languages/ka/audacity.mo deleted file mode 100644 index 31cc3243c6ac79c856628862c1ce460340e761a5..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ka/audacity.mo and /dev/null differ diff --git a/Release/Languages/km/audacity.mo b/Release/Languages/km/audacity.mo deleted file mode 100644 index c178d483a6f80dc9ca9c5307761a4899c89c33d2..0000000000000000000000000000000000000000 Binary files a/Release/Languages/km/audacity.mo and /dev/null differ diff --git a/Release/Languages/ko/audacity.mo b/Release/Languages/ko/audacity.mo deleted file mode 100644 index 0391d3dfb07a45814005d20666812008111f7829..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ko/audacity.mo and /dev/null differ diff --git a/Release/Languages/lt/audacity.mo b/Release/Languages/lt/audacity.mo deleted file mode 100644 index 6fdfbe025623b17d0a08f8c8ecaaaf6274cd820f..0000000000000000000000000000000000000000 Binary files a/Release/Languages/lt/audacity.mo and /dev/null differ diff --git a/Release/Languages/mk/audacity.mo b/Release/Languages/mk/audacity.mo deleted file mode 100644 index 2f9d3cfdf1b71585ec0a076ac3d33a2aeb087daa..0000000000000000000000000000000000000000 Binary files a/Release/Languages/mk/audacity.mo and /dev/null differ diff --git a/Release/Languages/mr/audacity.mo b/Release/Languages/mr/audacity.mo deleted file mode 100644 index 931fdb275ce88bc9506645b347ffc3601b444bf7..0000000000000000000000000000000000000000 Binary files a/Release/Languages/mr/audacity.mo and /dev/null differ diff --git a/Release/Languages/my/audacity.mo b/Release/Languages/my/audacity.mo deleted file mode 100644 index 5226392ed5da27b5f7c836bb6f56a27e95e024eb..0000000000000000000000000000000000000000 Binary files a/Release/Languages/my/audacity.mo and /dev/null differ diff --git a/Release/Languages/nb/audacity.mo b/Release/Languages/nb/audacity.mo deleted file mode 100644 index db3c3c18ba1a4e2a749e7a0de27d806f068a030f..0000000000000000000000000000000000000000 Binary files a/Release/Languages/nb/audacity.mo and /dev/null differ diff --git a/Release/Languages/nl/audacity.mo b/Release/Languages/nl/audacity.mo deleted file mode 100644 index 3e83e1d73e52f322900a698977367171502850c5..0000000000000000000000000000000000000000 Binary files a/Release/Languages/nl/audacity.mo and /dev/null differ diff --git a/Release/Languages/oc/audacity.mo b/Release/Languages/oc/audacity.mo deleted file mode 100644 index a90bc1eb7f99b6badec05e133bfc0d7e0ad8466e..0000000000000000000000000000000000000000 Binary files a/Release/Languages/oc/audacity.mo and /dev/null differ diff --git a/Release/Languages/pl/audacity.mo b/Release/Languages/pl/audacity.mo deleted file mode 100644 index 95203ebbf1d7af0fc25a33855d3d55f93d69d340..0000000000000000000000000000000000000000 Binary files a/Release/Languages/pl/audacity.mo and /dev/null differ diff --git a/Release/Languages/pt_BR/audacity.mo b/Release/Languages/pt_BR/audacity.mo deleted file mode 100644 index 00d56a97e46d5226ae9fa3565afa04f635dd5cb8..0000000000000000000000000000000000000000 Binary files a/Release/Languages/pt_BR/audacity.mo and /dev/null differ diff --git a/Release/Languages/pt_PT/audacity.mo b/Release/Languages/pt_PT/audacity.mo deleted file mode 100644 index 87cd0b56e1733fbc2c25bd8273a9a3fb228811af..0000000000000000000000000000000000000000 Binary files a/Release/Languages/pt_PT/audacity.mo and /dev/null differ diff --git a/Release/Languages/ro/audacity.mo b/Release/Languages/ro/audacity.mo deleted file mode 100644 index bcdd446eeea043b75af8e0624e270d833eb4e59f..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ro/audacity.mo and /dev/null differ diff --git a/Release/Languages/ru/audacity.mo b/Release/Languages/ru/audacity.mo deleted file mode 100644 index d59648b9dfaa5217b50e4ae468e477f09b95c51f..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ru/audacity.mo and /dev/null differ diff --git a/Release/Languages/sk/audacity.mo b/Release/Languages/sk/audacity.mo deleted file mode 100644 index 6c7ba98fd1925616177bb65647b53453555519b4..0000000000000000000000000000000000000000 Binary files a/Release/Languages/sk/audacity.mo and /dev/null differ diff --git a/Release/Languages/sl/audacity.mo b/Release/Languages/sl/audacity.mo deleted file mode 100644 index eb01b59051512703f5c8c1798c49e7b03653bd75..0000000000000000000000000000000000000000 Binary files a/Release/Languages/sl/audacity.mo and /dev/null differ diff --git a/Release/Languages/sr_RS/audacity.mo b/Release/Languages/sr_RS/audacity.mo deleted file mode 100644 index be8e9fae57149e61f99f8c597e6e046e5841aa0d..0000000000000000000000000000000000000000 Binary files a/Release/Languages/sr_RS/audacity.mo and /dev/null differ diff --git a/Release/Languages/sr_RS@latin/audacity.mo b/Release/Languages/sr_RS@latin/audacity.mo deleted file mode 100644 index c030cc81c6f1776970921d73d70c5ef0fe47fa1d..0000000000000000000000000000000000000000 Binary files a/Release/Languages/sr_RS@latin/audacity.mo and /dev/null differ diff --git a/Release/Languages/sv/audacity.mo b/Release/Languages/sv/audacity.mo deleted file mode 100644 index 77085ad35ad2dcd886f611079ba9669274ffd1c1..0000000000000000000000000000000000000000 Binary files a/Release/Languages/sv/audacity.mo and /dev/null differ diff --git a/Release/Languages/ta/audacity.mo b/Release/Languages/ta/audacity.mo deleted file mode 100644 index c1b4ab02d8c428ad1a870e517fa778296f632edc..0000000000000000000000000000000000000000 Binary files a/Release/Languages/ta/audacity.mo and /dev/null differ diff --git a/Release/Languages/tg/audacity.mo b/Release/Languages/tg/audacity.mo deleted file mode 100644 index 8badb1cad64b05d562e4a7e9f4dee9192cc247ef..0000000000000000000000000000000000000000 Binary files a/Release/Languages/tg/audacity.mo and /dev/null differ diff --git a/Release/Languages/tr/audacity.mo b/Release/Languages/tr/audacity.mo deleted file mode 100644 index ed416dc2ec7f8d1620773da5ae1e4bc560d887a1..0000000000000000000000000000000000000000 Binary files a/Release/Languages/tr/audacity.mo and /dev/null differ diff --git a/Release/Languages/uk/audacity.mo b/Release/Languages/uk/audacity.mo deleted file mode 100644 index 278ebe41918685d8be5e15816e6a185538ee23c3..0000000000000000000000000000000000000000 Binary files a/Release/Languages/uk/audacity.mo and /dev/null differ diff --git a/Release/Languages/vi/audacity.mo b/Release/Languages/vi/audacity.mo deleted file mode 100644 index c34aba21c9245d9d4a91fea032992e8853d92d48..0000000000000000000000000000000000000000 Binary files a/Release/Languages/vi/audacity.mo and /dev/null differ diff --git a/Release/Languages/zh_CN/audacity.mo b/Release/Languages/zh_CN/audacity.mo deleted file mode 100644 index 42b2bba6c973650f95ba433e3970ee71709cfc24..0000000000000000000000000000000000000000 Binary files a/Release/Languages/zh_CN/audacity.mo and /dev/null differ diff --git a/Release/Languages/zh_TW/audacity.mo b/Release/Languages/zh_TW/audacity.mo deleted file mode 100644 index 953ec249a1a179e1d5d59fdb604fb0bb5071234f..0000000000000000000000000000000000000000 Binary files a/Release/Languages/zh_TW/audacity.mo and /dev/null differ diff --git a/Release/nyquist/aud-do-support.lsp b/Release/nyquist/aud-do-support.lsp deleted file mode 100644 index 23981f5796a210f89ffd49a30fd9b7a6f6f5c79a..0000000000000000000000000000000000000000 --- a/Release/nyquist/aud-do-support.lsp +++ /dev/null @@ -1,236 +0,0 @@ -;;; A collection of helper functions and macros to make scripting Audacity commands -;;; easier and more Lisp-like. -;;; -;;; Copyright 2018 - 2020 Audacity Team -;;; Steve Daulton -;;; Released under terms of the GNU General Public License version 2: -;;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - - -(defun char-remove (ch str) - ;;; Remove all occurrences of character from string. - (do ((out "") - (i 0 (1+ i))) - ((= i (length str)) out) - (if (char/= (char str i) ch) - (setf out (format nil "~a~a" out (char str i)))))) - -(defun number-string-p (str) - ;;; like digit-char-p for strings - (unless (stringp str) - (return-from number-string-p nil)) - (let ((num (string-to-number str))) - (if (numberp num) - num - nil))) - -(defmacro string-append (str &rest strs) - ;;; Append one or more strings to 'str' - `(setf ,str (strcat ,str ,@strs))) - -(defun aud-print-command (cmd) - ;;; Print a quick reference for command arguments. - (let ((help-data (first (aud-do-command "Help" :command cmd :format "LISP"))) - (out (format nil "(aud-do-command ~s [:key val ...])~%" (string-downcase cmd)))) - (cond - ((string-equal help-data "Command not found") - ;Debug out can be copied on all platforms. - (format t "~a~a." out help-data) - (format nil "~a~a." out help-data)) - (t (setf help-data (eval-string (quote-string help-data))) - (let ((params (second (assoc 'params help-data)))) - (dolist (p params) - (setf out (format nil "~a :~a (~a) default: ~s~%" - out - (string-downcase (second (assoc 'key p))) - (second (assoc 'type p)) - (second (assoc 'default p)))) - (let ((enums (assoc 'enum p))) - (when enums - (setf out (format nil "~a [" out)) - (dolist (e (second enums)) - (setf out (format nil "~a~s " out e))) - (setf out (format nil "~a]~%" (string-right-trim " " out))))))) - (format t "~a" out) - out)))) - - -(defun aud-do-command (id &rest params) - ;; Translate aud-do-command, to (aud-do "command"). - ;; To avoid unnecessary overhead, only validate when debugging enabled - ;; 'aud-import-commands' passes params as a list, so we need to unpack it. - (when (and (= (length params) 1) - (listp (first params))) - (setf params (first params))) - (when *tracenable* - (aud-check-debug-cache) - (let (val-allowed type enums pstr - (id-valid (aud-verify-command-id id)) - (valid-params (aud-get-command-params id)) - (keystr "")) - (if (not id-valid) - ; The command may still be valid as - ; "GetInfo: Type=Commands" does not return all valid AUD-DO commands. - (format t "Debug data unavailable: ~s.~%" id) - ;; Command ID recognised, so check params. - (dolist (p params) - (setf pstr (format nil "~a" p)) - (cond - ((char= (char pstr 0) #\:) ;keyword - (setf keystr (subseq pstr 1)) - (let ((kf (dolist (vp valid-params nil) - (when (string-equal (second (assoc 'key vp)) keystr) - (return vp))))) - (cond - (kf ;keyword found - (setf type (second (assoc 'type kf))) - (setf enums (second (assoc 'enum kf))) - (cond - ((member type '("int" "float" "double") :test 'string-equal) - (setf val-allowed "number")) - ((string-equal type "enum") - (setf val-allowed enums)) ;a list - (t (setf val-allowed type)))) ;"string" "bool" or NIL - ;; Invalid keyword, so give some helpful hints: - (t (format t "Invalid key in ~s :~a~%" id keystr) - ;; pretty print valid keywords - (format t "Valid keys for ~a are:~%" id) - (dolist (vp valid-params) - (dolist (item vp) - (let ((itype (first item))) - (case itype - ('KEY (format t " ~a " (second item))) - ('TYPE (when (string-not-equal (second item) "enum") - (format t "(~a) " (second item)))) - ('ENUM (format t "[~a]" - (string-trim "()" - (format nil "~a" (second item)))))))) - (format t "~%")))))) - (t ;key value - (cond - ((not val-allowed) - (format t "Too many arguments: ~s :~a~%" id keystr)) - ((listp val-allowed) - (unless (member pstr enums :test 'string=) ;case sensitive - (format t "Invalid enum in ~s :~a - ~s~%" id keystr p) - (format t "Options are:~% ~a~%" enums))) - ((string= val-allowed "bool") - (unless (or (string= pstr "0") (string= pstr "1")) - (format t "~s :~a value must be 0 or 1~%" id keystr))) - ((string= val-allowed "number") - (unless (or (numberp p) (number-string-p p)) - (format t "~s :~a value must be a number: ~s~%" id keystr p))) - ((string= val-allowed "string") - (unless (stringp p) - (format t "~s :~a value must be a string: ~a~%" id keystr p)))) - (psetq val-allowed nil - type nil - enums nil))))))) - ;; Send the command - (let ((cmd (format nil "~a:" id))) - (dolist (p params) - (setf p (format nil "~a" p)) - (string-append cmd - (cond - ((char= (char p 0) #\:) ;keyword - (format nil " ~a=" (subseq p 1))) - (t ;key value - (format nil "~s" p))))) - (aud-do cmd))) - - -(defun aud-import-commands (&aux cmd) - ;; Generate function stubs in the form (aud-<command> [&key arg ...]) - ;; Call once to make "aud-<command>"s available. - ;; We don't call this on load, as we don't want to delay loading Nyquist unnecessarily. - (aud-check-debug-cache) - (dolist (cmd (aud-get-command)) - (setf cmd (second (assoc 'id cmd))) - (let ((symb (intern (string-upcase (format nil "aud-~a" cmd))))) - (eval `(defun ,symb (&rest args) - (aud-do-command ,cmd args)))))) - - -(defun aud-check-debug-cache () - ;;; Load aud-do-debug-data-cache, updating if necessary. - (let ((fqname (format nil "~a~a~a" - (string-right-trim (string *file-separator*) (get-temp-path)) - *file-separator* - "aud-do-debug-data-cache.lsp"))) - (cond ;Update if necessary - ((fboundp 'aud-do-version) ;cache is loaded - ;; Refresh cache if versions don't match. - ;; 'aud-do-version' tests the interned version. - ;; 'autoload-helper' tests the disk version and prevents repeating cache refresh in the initial session. - (unless (or (string= (format nil "~a" (aud-do-version)) - (format nil "~a" (get '*audacity* 'version))) - (string= (format nil "~a" (autoload-helper fqname 'aud-do-version nil)) - (format nil "~a" (get '*audacity* 'version)))) - (aud-refresh-debug-data-cache fqname))) - ;cache not loaded, so try loading and refresh if we can't. - ((not (load fqname :verbose t)) - (aud-refresh-debug-data-cache fqname))))) - - -(defun aud-refresh-debug-data-cache (fqname) - ;; Cache the list of command profiles as function "aud-get-command", and load it. - (labels ((disable-plugins (typestring &aux oldval) - ;; Disable plug-ins of type 'typestring' and return it's previous value. - (let ((getcmd (format nil "GetPreference: Name=\"~a/Enable\"" typestring))) - (setf oldval (first (aud-do getcmd))) - (do-set-val typestring oldval 0) ;Disable all plug-ins - oldval)) ;may be 0, 1 or "" - (do-set-val (typestring oldval newval) - ;; If plug-in type was previously enabled ('oldval = true, "1" or empty), set it to 'newval'. - (let ((setcmd (format nil "SetPreference: Name=\"/~a/Enable\" Value=" typestring))) - (when (and oldval (or (string= oldval "")(string= oldval "1"))) - (aud-do (format nil "~a~s" setcmd (if (= newval 0) 0 oldval)))))) - (get-usable-commands () - ;; Disable plug-ins, get list of remaining commands, then re-enable plug-ins if previously enabled. - ;; Return list of commands. - (let ((cmds '(("Nyquist" ny)("LADSPA" la)("LV2" lv)("VST" vs)("AudioUnit" au)("Vamp" va))) - info) - (dolist (cmd cmds) - (setf (nth 1 cmd) (disable-plugins (nth 0 cmd)))) - (setf info (first (aud-do "getinfo: type=Commands format=LISP"))) ;Get scriptables and built-in effects - (dolist (cmd cmds) - (do-set-val (nth 0 cmd) (nth 1 cmd) 1)) ;Re-enable plug-ins - info))) - (let ((fp (open fqname :direction :output))) - ;; Write cache file, or return error. - (cond - (fp (format fp -";; Intended for internal use by aud-do-command.~% -(defun aud-do-version () - '~a)~% -(defun aud-verify-command-id (id) - (second (assoc 'id (aud-get-command id))))~% -(defun aud-get-command-params (id) - (second (assoc 'params (aud-get-command id))))~% -(defun aud-get-command (&optional id &aux cmds) - ;; If id supplied, return command profile or nil. - ;; Else, return full list. - (setf cmds - '~a) - ;; Return all commands, or one command or nil. - (if id - (dolist (cmd cmds nil) - (when (string-equal (string id) (second (assoc 'id cmd))) - (return cmd))) - cmds))" - (get '*audacity* 'version) - (get-usable-commands)) - (format t "Debug data cache refreshed.~%") - (close fp) - (unless (load fqname :verbose t) ;load the file - (error "Unable to load" fqname))) ;assert - (t (format t "Error: ~a cannot be written." fqname)))))) - - -;; Try to load AUD- command cache. -(when (get-temp-path) - (let ((fqname (format nil "~a~a~a" - (string-right-trim (string *file-separator*) (get-temp-path)) - *file-separator* - "aud-do-debug-data-cache.lsp"))) - (load fqname :verbose t))) diff --git a/Release/nyquist/dspprims.lsp b/Release/nyquist/dspprims.lsp deleted file mode 100644 index 2085556acfee43c961dd63431fa3b49c763796bc..0000000000000000000000000000000000000000 --- a/Release/nyquist/dspprims.lsp +++ /dev/null @@ -1,728 +0,0 @@ -;; dspprims.lsp -- interface to dsp primitives - -;; ARESON - notch filter -;; -(defun areson (s c b &optional (n 0)) - (multichan-expand "ARESON" #'nyq:areson - '(((SOUND) nil) ((NUMBER SOUND) "center") - ((NUMBER SOUND) "bandwidth") ((INTEGER) nil)) - s c b n)) - -(setf areson-implementations - (vector #'snd-areson #'snd-aresonvc #'snd-aresoncv #'snd-aresonvv)) - -;; NYQ:ARESON - notch filter, single channel -;; -(defun nyq:areson (signal center bandwidth normalize) - (select-implementation-1-2 "ARESON" areson-implementations - signal center bandwidth normalize)) - - -;; hp - highpass filter -;; -(defun hp (s c) - (multichan-expand "HP" #'nyq:hp - '(((SOUND) "snd") ((NUMBER SOUND) "cutoff")) s c)) - -(setf hp-implementations - (vector #'snd-atone #'snd-atonev)) - -;; NYQ:hp - highpass filter, single channel -;; -(defun nyq:hp (s c) - (select-implementation-1-1 "HP" hp-implementations s c)) - - -;; comb-delay-from-hz -- compute the delay argument -;; -(defun comb-delay-from-hz (hz) - (recip hz)) - -;; comb-feedback -- compute the feedback argument -;; -(defun comb-feedback (decay delay) - (s-exp (mult -6.9087 delay (recip decay)))) - -;; COMB - comb filter -;; -;; this is just a feedback-delay with different arguments -;; -(defun comb (snd decay hz) - (multichan-expand "COMB" #'nyq:comb - '(((SOUND) "snd") ((NUMBER SOUND) "decay") ((POSITIVE) "hz")) - snd decay hz)) - - -(defun nyq:comb (snd decay hz) - (let (delay feedback len d) - ; convert decay to feedback - (setf delay (/ (float hz))) - (setf feedback (comb-feedback decay delay)) - (nyq:feedback-delay snd delay feedback "COMB"))) - -;; ALPASS - all-pass filter -;; -(defun alpass (snd decay hz &optional min-hz) - (multichan-expand "ALPASS" #'nyq:alpass - '(((SOUND) "snd") ((NUMBER SOUND) "decay") - ((POSITIVE SOUND) "hz") ((POSITIVE-OR-NULL) "min-hz")) - snd decay hz min-hz)) - -(defun nyq:alpass (snd decay hz min-hz) - (let (delay feedback len d) - ; convert decay to feedback, iterate over array if necessary - (setf delay (comb-delay-from-hz hz)) - (setf feedback (comb-feedback decay delay)) - (nyq:alpass1 snd delay feedback min-hz))) - - -;; CONST -- a constant at control-srate -;; -(defun const (value &optional (dur 1.0)) - (ny:typecheck (not (numberp value)) - (ny:error "CONST" 1 '((NUMBER) "value") value)) - (ny:typecheck (not (numberp dur)) - (ny:error "CONST" 2 '((NUMBER) "dur") dur)) - (let ((d (get-duration dur))) - (snd-const value *rslt* *CONTROL-SRATE* d))) - - -;; CONVOLVE - fast convolution -;; -(defun convolve (s r) - (multichan-expand "CONVOLVE" #'nyq:convolve - '(((SOUND) nil) ((SOUND) nil)) s r)) - -(defun nyq:convolve (s r) - (snd-convolve s (force-srate (snd-srate s) r))) - - -;; FEEDBACK-DELAY -- (delay is quantized to sample period) -;; -(defun feedback-delay (snd delay feedback) - (multichan-expand "FEEDBACK-DELAY" #'nyq:feedback-delay - '(((SOUND) "snd") ((NUMBER) "delay") ((NUMBER SOUND) "feedback")) - snd delay feedback)) - - -;; SND-DELAY-ERROR -- report type error -;; -(defun snd-delay-error (snd delay feedback) - (error "FEEDBACK-DELAY with variable delay is not implemented")) - - -(setf feedback-delay-implementations - (vector #'snd-delay #'snd-delay-error #'snd-delaycv #'snd-delay-error)) - - -;; NYQ:FEEDBACK-DELAY -- single channel delay -;; -(defun nyq:feedback-delay (snd delay feedback &optional (src "FEEDBACK-DELAY")) - (select-implementation-1-2 src feedback-delay-implementations - snd delay feedback)) - - -;; SND-ALPASS-ERROR -- report type error -;; -(defun snd-alpass-error (snd delay feedback) - (error "ALPASS with constant decay and variable hz is not implemented")) - - -(if (not (fboundp 'snd-alpasscv)) - (defun snd-alpasscv (snd delay feedback min-hz) - (error "snd-alpasscv (ALPASS with variable decay) is not implemented"))) -(if (not (fboundp 'snd-alpassvv)) - (defun snd-alpassvv (snd delay feedback min-hz) - (error "snd-alpassvv (ALPASS with variable decay and feedback) is not implemented"))) - - -(defun nyq:alpassvv (the-snd delay feedback min-hz) - (let (max-delay) - (ny:typecheck (or (not (numberp min-hz)) (<= min-hz 0)) - (ny:error "ALPASS" 4 '((POSITIVE) "min-hz") min-hz)) - (setf max-delay (/ (float min-hz))) - ; make sure delay is between 0 and max-delay - ; use clip function, which is symmetric, with an offset - (setf delay (snd-offset (clip (snd-offset delay (* max-delay -0.5)) - (* max-delay 0.5)) - (* max-delay 0.5))) - ; now delay is between 0 and max-delay, so we won't crash nyquist when - ; we call snd-alpassvv, which doesn't test for out-of-range data - (snd-alpassvv the-snd delay feedback max-delay))) - - -;; NYQ:SND-ALPASS -- ignores min-hz argument and calls snd-alpass -;; -(defun nyq:snd-alpass (snd delay feedback min-hz) - (snd-alpass snd delay feedback)) - -;; NYQ:SND-ALPASSCV -- ignores min-hz argument and calls snd-alpasscv -;; -(defun nyq:snd-alpasscv (snd delay feedback min-hz) - (snd-alpasscv snd delay feedback)) - -(setf alpass-implementations - (vector #'nyq:snd-alpass #'snd-alpass-error - #'nyq:snd-alpasscv #'nyq:alpassvv)) - - -;; NYQ:ALPASS1 -- single channel alpass -;; -(defun nyq:alpass1 (snd delay feedback min-hz) - (select-implementation-1-2 "ALPASS" alpass-implementations - snd delay feedback min-hz)) - -;; CONGEN -- contour generator, patterned after gated analog env gen -;; -(defun congen (gate rise fall) - (multichan-expand "CONGEN" #'snd-congen - '(((SOUND) "gate") ((NONNEGATIVE) "rise") ((NONNEGATIVE) "fall")) - gate rise fall)) - - -;; S-EXP -- exponentiate a sound -;; -(defun s-exp (s) - (multichan-expand "S-EXP" #'nyq:exp - '(((NUMBER SOUND) nil)) s)) - - -;; NYQ:EXP -- exponentiate number or sound -;; -(defun nyq:exp (s) (if (soundp s) (snd-exp s) (exp s))) - -;; S-ABS -- absolute value of a sound -;; -(defun s-abs (s) - (multichan-expand "S-ABS" #'nyq:abs - '(((NUMBER SOUND) nil)) s)) - -;; NYQ:ABS -- absolute value of number or sound -;; -(defun nyq:abs (s) - (if (soundp s) (snd-abs s) (abs s))) - -;; S-AVG -- moving average or peak computation -;; -(defun s-avg (s blocksize stepsize operation) - (multichan-expand "S-AVG" #'snd-avg - '(((SOUND) nil) ((INTEGER) "blocksize") ((INTEGER) "stepsize") - ((INTEGER) "operation")) - s blocksize stepsize operation)) - -;; S-SQRT -- square root of a sound -;; -(defun s-sqrt (s) - (multichan-expand "S-SQRT" #'nyq:sqrt - '(((NUMBER SOUND) nil)) s)) - - -;; NYQ:SQRT -- square root of a number or sound -;; -(defun nyq:sqrt (s) - (if (soundp s) (snd-sqrt s) (sqrt s))) - - -;; INTEGRATE -- integration -;; -(defun integrate (s) - (multichan-expand "INTEGRATE" #'snd-integrate - '(((SOUND) nil)) s)) - - -;; S-LOG -- natural log of a sound -;; -(defun s-log (s) - (multichan-expand "S-LOG" #'nyq:log - '(((NUMBER SOUND) nil)) s)) - - -;; NYQ:LOG -- log of a number or sound -;; -(defun nyq:log (s) - (if (soundp s) (snd-log s) (log s))) - - -;; NOISE -- white noise -;; -(defun noise (&optional (dur 1.0)) - (ny:typecheck (not (numberp dur)) - (ny:error "NOISE" 1 number-anon dur)) - (let ((d (get-duration dur))) - (snd-white *rslt* *SOUND-SRATE* d))) - - -(defun noise-gate (snd &optional (lookahead 0.5) (risetime 0.02) (falltime 0.5) - (floor 0.01) (threshold 0.01) &key (rms nil) (link t)) - (let ((sense (if rms (rms snd 100.0 nil "NOISE-GATE") (s-abs snd)))) - (cond (link - (mult snd (gate sense lookahead risetime falltime floor - threshold "NOISE-GATE"))) - (t - (mult snd (multichan-expand "NOISE-GATE" #'gate - '(((SOUND) "sound") ((NUMBER) "lookahead") - ((NUMBER) "risetime") ((NUMBER) "falltime") - ((NUMBER) "floor") ((NUMBER) "threshold") - ((STRING) "source")) - sense lookahead risetime falltime - floor threshold "NOISE-GATE")))))) - - -;; QUANTIZE -- quantize a sound -;; -(defun quantize (s f) - (multichan-expand "QUANTIZE" #'snd-quantize - '(((SOUND) nil) ((POSITIVE) nil)) s f)) - - -;; RECIP -- reciprocal of a sound -;; -(defun recip (s) - (multichan-expand "RECIP" #'nyq:recip - '(((NUMBER SOUND) nil)) s)) - - -;; NYQ:RECIP -- reciprocal of a number or sound -;; -(defun nyq:recip (s) - (if (soundp s) (snd-recip s) (/ (float s)))) - - - -;; RMS -- compute the RMS of a sound -;; -(defun rms (s &optional (rate 100.0) window-size (source "RMS")) - (multichan-expand "RMS" #'ny:rms - '(((SOUND) nil) ((POSITIVE) "rate") ((POSITIVE-OR-NULL) "window-size") - ((STRING) "source")) - s rate window-size source)) - - -;; NY:RMS -- single channel RMS -;; -(defun ny:rms (s &optional (rate 100.0) window-size source) - (let (rslt step-size) - (ny:typecheck (not (or (soundp s) (multichannel-soundp s))) - (ny:error source 1 '((SOUND) NIL) s t)) - (ny:typecheck (not (numberp rate)) - (ny:error source 2 '((NUMBER) "rate") rate)) - (setf step-size (round (/ (snd-srate s) rate))) - (cond ((null window-size) - (setf window-size step-size)) - ((not (integerp window-size)) - (ny:error source 3 '((INTEGER) "window-size" window-size)))) - (setf s (prod s s)) - (setf result (snd-avg s window-size step-size OP-AVERAGE)) - ;; compute square root of average - (s-exp (scale 0.5 (s-log result))))) - - -;; RESON - bandpass filter -;; -(defun reson (s c b &optional (n 0)) - (multichan-expand "RESON" #'nyq:reson - '(((SOUND) "snd") ((NUMBER SOUND) "center") - ((NUMBER SOUND) "bandwidth") ((INTEGER) "n")) - s c b n)) - - -(setf reson-implementations - (vector #'snd-reson #'snd-resonvc #'snd-resoncv #'snd-resonvv)) - -;; NYQ:RESON - bandpass filter, single channel -;; -(defun nyq:reson (signal center bandwidth normalize) - (select-implementation-1-2 "RESON" reson-implementations - signal center bandwidth normalize)) - - -;; SHAPE -- waveshaper -;; -(defun shape (snd shape origin) - (multichan-expand "SHAPE" #'snd-shape - '(((SOUND) "snd") ((SOUND) "shape") ((NUMBER) "origin")) - snd shape origin)) - - -;; SLOPE -- calculate the first derivative of a signal -;; -(defun slope (s) - (multichan-expand "SLOPE" #'nyq:slope - '(((SOUND) nil)) s)) - - -;; NYQ:SLOPE -- first derivative of single channel -;; -(defun nyq:slope (s) - (let* ((sr (snd-srate s)) - (sr-inverse (/ sr))) - (snd-xform (snd-slope s) sr 0 sr-inverse MAX-STOP-TIME 1.0))) - - -;; lp - lowpass filter -;; -(defun lp (s c) - (multichan-expand "LP" #'nyq:lp - '(((SOUND) "snd") ((NUMBER SOUND) "cutoff")) s c)) - -(setf lp-implementations - (vector #'snd-tone #'snd-tonev)) - -;; NYQ:lp - lowpass filter, single channel -;; -(defun nyq:lp (s c) - (select-implementation-1-1 "LP" lp-implementations s c)) - - - -;;; fixed-parameter filters based on snd-biquad -;;; note: snd-biquad is implemented in biquadfilt.[ch], -;;; while BiQuad.{cpp,h} is part of STK - -(setf Pi 3.14159265358979) - -(defun square (x) (* x x)) -(defun sinh (x) (* 0.5 (- (exp x) (exp (- x))))) - - -; remember that snd-biquad uses the opposite sign convention for a_i's -; than Matlab does. -; -; Stability: Based on courses.cs.washington.edu/courses/cse490s/11au/ -; Readings/Digital_Sound_Generation_2.pdf, the stable region is -; (a2 < 1) and ((a2 + 1) > |a1|) -; It doesn't look to me like our a0, a1, a2 match the paper's a0, a1, a2, -; and I'm not convinced the paper's derivation is correct, but at least -; the predicted region of stability is correct if we swap signs on a1 and -; a2 (but due to the |a1| term, only the sign of a2 matters). This was -; tested manually at a number of points inside and outside the stable -; triangle. Previously, the stability test was (>= a0 1.0) which seems -; generally wrong. The old test has been removed. - -; convenient biquad: normalize a0, and use zero initial conditions. -(defun nyq:biquad (x b0 b1 b2 a0 a1 a2) - (ny:typecheck (<= a0 0.0) - (error (format nil "In BIQUAD, a0 < 0 (unstable parameter a0 = ~A)" a0))) - (let ((a0r (/ (float a0)))) - (setf a1 (* a0r a1) - a2 (* a0r a2)) - (ny:typecheck (or (<= a2 -1.0) (<= (- 1.0 a2) (abs a1))) - (error (format nil - "In BIQUAD, (a2 <= -1) or (1 - a2 <= |a1|) (~A a1 = ~A, a2 = ~A)" - "unstable parameters" a1 a2))) - (snd-biquad x (* a0r b0) (* a0r b1) (* a0r b2) - a1 a2 0 0))) - - -(defun biquad (x b0 b1 b2 a0 a1 a2 &optional (source "BIQUAD")) - (multichan-expand "BIQUAD" #'nyq:biquad - '(((SOUND) "snd") ((NUMBER) "b0") ((NUMBER) "b1") - ((NUMBER) "b2") ((NUMBER) "a0") ((NUMBER) "a1") - ((NUMBER) "a2")) - x b0 b1 b2 a0 a1 a2)) - - -; biquad with Matlab sign conventions for a_i's. -(defun biquad-m (x b0 b1 b2 a0 a1 a2) - (multichan-expand "BIQUAD-M" #'nyq:biquad-m - '(((SOUND) "snd") ((NUMBER) "b0") ((NUMBER) "b1") - ((NUMBER) "b2") ((NUMBER) "a0") ((NUMBER) "a1") - ((NUMBER) "a2")) - x b0 b1 b2 a0 a1 a2)) - -(defun nyq:biquad-m (x b0 b1 b2 a0 a1 a2 &optional (source "BIQUAD-M")) - (nyq:biquad x b0 b1 b2 a0 (- a1) (- a2))) - -; two-pole lowpass -(defun lowpass2 (x hz &optional (q 0.7071) (source "LOWPASS2")) - (multichan-expand source #'nyq:lowpass2 - '(((SOUND) "snd") ((POSITIVE) "hz") ((POSITIVE) "q") ((STRING) "source")) - x hz q source)) - -;; NYQ:LOWPASS2 -- operates on single channel -(defun nyq:lowpass2 (x hz q source) - (if (or (> hz (* 0.5 (snd-srate x))) - (< hz 0)) - (error "cutoff frequency out of range" hz)) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (cw (cos w)) - (sw (sin w)) - (alpha (* sw (sinh (/ 0.5 q)))) - (a0 (+ 1.0 alpha)) - (a1 (* -2.0 cw)) - (a2 (- 1.0 alpha)) - (b1 (- 1.0 cw)) - (b0 (* 0.5 b1)) - (b2 b0)) - (nyq:biquad-m x b0 b1 b2 a0 a1 a2 source))) - -; two-pole highpass -(defun highpass2 (x hz &optional (q 0.7071) (source "HIGHPASS2")) - (multichan-expand source #'nyq:highpass2 - '(((SOUND) "snd") ((POSITIVE) "hz") ((POSITIVE) "q") ((STRING) "source")) - x hz q source)) - -(defun nyq:highpass2 (x hz q source) - (if (or (> hz (* 0.5 (snd-srate x))) - (< hz 0)) - (error "cutoff frequency out of range" hz)) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (cw (cos w)) - (sw (sin w)) - (alpha (* sw (sinh (/ 0.5 q)))) - (a0 (+ 1.0 alpha)) - (a1 (* -2.0 cw)) - (a2 (- 1.0 alpha)) - (b1 (- -1.0 cw)) - (b0 (* -0.5 b1)) - (b2 b0)) - (nyq:biquad-m x b0 b1 b2 a0 a1 a2 source))) - -; two-pole bandpass. max gain is unity. -(defun bandpass2 (x hz q) - (multichan-expand "BANDPASS2" #'nyq:bandpass2 - '(((SOUND) "snd") ((POSITIVE) "hz") ((POSITIVE) "q")) - x hz q)) - -(defun nyq:bandpass2 (x hz q) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (cw (cos w)) - (sw (sin w)) - (alpha (* sw (sinh (/ 0.5 q)))) - (a0 (+ 1.0 alpha)) - (a1 (* -2.0 cw)) - (a2 (- 1.0 alpha)) - (b0 alpha) - (b1 0.0) - (b2 (- alpha))) - (nyq:biquad-m x b0 b1 b2 a0 a1 a2 "BANDPASS2"))) - -; two-pole notch. -(defun notch2 (x hz q) - (multichan-expand "NOTCH2" #'nyq:notch2 - '(((SOUND) "snd") ((POSITIVE) "hz") ((POSITIVE) "q")) - x hz q)) - -(defun nyq:notch2 (x hz q) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (cw (cos w)) - (sw (sin w)) - (alpha (* sw (sinh (/ 0.5 q)))) - (a0 (+ 1.0 alpha)) - (a1 (* -2.0 cw)) - (a2 (- 1.0 alpha)) - (b0 1.0) - (b1 (* -2.0 cw)) - (b2 1.0)) - (nyq:biquad-m x b0 b1 b2 a0 a1 a2 "NOTCH2"))) - - -; two-pole allpass. -(defun allpass2 (x hz q) - (multichan-expand "ALLPASS2" #'nyq:allpass - '(((SOUND) "snd") ((POSITIVE) "hz") ((POSITIVE) "q")) - x hz q)) - -(defun nyq:allpass (x hz q) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (cw (cos w)) - (sw (sin w)) - (k (exp (* -0.5 w (/ (float q))))) - (a0 1.0) - (a1 (* -2.0 cw k)) - (a2 (* k k)) - (b0 a2) - (b1 a1) - (b2 1.0)) - (nyq:biquad-m x b0 b1 b2 a0 a1 a2 "ALLPASS2"))) - - -; bass shelving EQ. gain in dB; Fc is halfway point. -; response becomes peaky at slope > 1. -(defun eq-lowshelf (x hz gain &optional (slope 1.0)) - (multichan-expand "EQ-LOWSHELF" #'nyq:eq-lowshelf - '(((SOUND) "snd") ((POSITIVE) "hz") ((NUMBER) "gain") ((NUMBER) "slope")) - x hz gain slope)) - - -(defun nyq:eq-lowshelf (x hz gain slope) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (sw (sin w)) - (cw (cos w)) - (A (expt 10.0 (/ gain (* 2.0 20.0)))) - (b (sqrt (- (/ (+ 1.0 (square A)) slope) (square (- A 1.0))))) - (apc (* cw (+ A 1.0))) - (amc (* cw (- A 1.0))) - (bs (* b sw)) - - (b0 (* A (+ A 1.0 (- amc) bs ))) - (b1 (* 2.0 A (+ A -1.0 (- apc) ))) - (b2 (* A (+ A 1.0 (- amc) (- bs) ))) - (a0 (+ A 1.0 amc bs )) - (a1 (* -2.0 (+ A -1.0 apc ))) - (a2 (+ A 1.0 amc (- bs) ))) - (nyq:biquad-m x b0 b1 b2 a0 a1 a2))) - - -; treble shelving EQ. gain in dB; Fc is halfway point. -; response becomes peaky at slope > 1. -(defun eq-highshelf (x hz gain &optional (slope 1.0)) - (multichan-expand "EQ-HIGHSHELF" #'nyq:eq-highshelf - '(((SOUND) "snd") ((POSITIVE) "hz") ((NUMBER) "gain") ((NUMBER) "slope")) - x hz gain slope)) - -(defun nyq:eq-highshelf (x hz gain slope) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (sw (sin w)) - (cw (cos w)) - (A (expt 10.0 (/ gain (* 2.0 20.0)))) - (b (sqrt (- (/ (+ 1.0 (square A)) slope) (square (- A 1.0))))) - (apc (* cw (+ A 1.0))) - (amc (* cw (- A 1.0))) - (bs (* b sw)) - - (b0 (* A (+ A 1.0 amc bs ))) - (b1 (* -2.0 A (+ A -1.0 apc ))) - (b2 (* A (+ A 1.0 amc (- bs) ))) - (a0 (+ A 1.0 (- amc) bs )) - (a1 (* 2.0 (+ A -1.0 (- apc) ))) - (a2 (+ A 1.0 (- amc) (- bs) ))) - (nyq:biquad-m x b0 b1 b2 a0 a1 a2))) - -(defun nyq:eq-band (x hz gain width) - (cond ((and (numberp hz) (numberp gain) (numberp width)) - (eq-band-ccc x hz gain width)) - ((and (soundp hz) (soundp gain) (soundp width)) - (snd-eqbandvvv x hz (db-to-linear gain) width)) - (t (error - (strcat - "In EQ-BAND, hz, gain, and width must be all numbers" - " or all sounds (if any parameter is an array, there" - " is a problem with at least one channel), hz is " - (param-to-string hz) ", gain is " (param-to-string gain) - ", width is " (param-to-string width)) )) )) - -; midrange EQ. gain in dB, width in octaves (half-gain width). -(defun eq-band (x hz gain width) - (multichan-expand "EQ-BAND" #'nyq:eq-band - '(((SOUND) "snd") ((POSITIVE SOUND) "hz") - ((NUMBER SOUND) "gain") ((POSITIVE SOUND) "width")) - x hz gain width)) - - -(defun eq-band-ccc (x hz gain width) - (let* ((w (* 2.0 Pi (/ hz (snd-srate x)))) - (sw (sin w)) - (cw (cos w)) - (J (sqrt (expt 10.0 (/ gain 20.0)))) - ;(dummy (display "eq-band-ccc" gain J)) - (g (* sw (sinh (* 0.5 (log 2.0) width (/ w sw))))) - ;(dummy2 (display "eq-band-ccc" width w sw g)) - (b0 (+ 1.0 (* g J))) - (b1 (* -2.0 cw)) - (b2 (- 1.0 (* g J))) - (a0 (+ 1.0 (/ g J))) - (a1 (- b1)) - (a2 (- (/ g J) 1.0))) - (biquad x b0 b1 b2 a0 a1 a2))) - -; see failed attempt in eub-reject.lsp to do these with higher-order fns: - -; four-pole Butterworth lowpass -(defun lowpass4 (x hz) - (lowpass2 (lowpass2 x hz 0.60492333 "LOWPASS4") - hz 1.33722126 "LOWPASS4")) - -; six-pole Butterworth lowpass -(defun lowpass6 (x hz) - (lowpass2 (lowpass2 (lowpass2 x hz 0.58338080 "LOWPASS6") - hz 0.75932572 "LOWPASS6") - hz 1.95302407 "LOWPASS6")) - -; eight-pole Butterworth lowpass -(defun lowpass8 (x hz) - (lowpass2 (lowpass2 (lowpass2 (lowpass2 x hz 0.57622191 "LOWPASS8") - hz 0.66045510 "LOWPASS8") - hz 0.94276399 "LOWPASS8") - hz 2.57900101 "LOWPASS8")) - -; four-pole Butterworth highpass -(defun highpass4 (x hz) - (highpass2 (highpass2 x hz 0.60492333 "HIGHPASS4") - hz 1.33722126 "HIGHPASS4")) - -; six-pole Butterworth highpass -(defun highpass6 (x hz) - (highpass2 (highpass2 (highpass2 x hz 0.58338080 "HIGHPASS6") - hz 0.75932572 "HIGHPASS6") - hz 1.95302407 "HIGHPASS6")) - -; eight-pole Butterworth highpass -(defun highpass8 (x hz) - (highpass2 (highpass2 (highpass2 (highpass2 x hz 0.57622191 "HIGHPASS8") - hz 0.66045510 "HIGHPASS8") - hz 0.94276399 "HIGHPASS8") - hz 2.57900101 "HIGHPASS8")) - -; YIN -; maybe this should handle multiple channels, etc. -(defun yin (sound minstep maxstep stepsize) - (ny:typecheck (not (soundp sound)) - (ny:error "YIN" 1 '((SOUND) "sound") sound)) - (ny:typecheck (not (numberp minstep)) - (ny:error "YIN" 2 '((NUMBER) "minstep") minstep)) - (ny:typecheck (not (numberp maxstep)) - (ny:error "YIN" 3 '((NUMBER) "maxstep") maxstep)) - (ny:typecheck (not (integerp stepsize)) - (ny:error "YIN" 4 '((INTEGER) "stepsize") stepsize)) - (snd-yin sound minstep maxstep stepsize)) - - -; FOLLOW -(defun follow (sound floor risetime falltime lookahead) - (ny:typecheck (not (soundp sound)) - (ny:error "FOLLOW" 1 '((SOUND) "sound") sound)) - (ny:typecheck (not (numberp floor)) - (ny:error "FOLLOW" 2 '((NUMBER) "floor") floor)) - (ny:typecheck (not (numberp risetime)) - (ny:error "FOLLOW" 3 '((NUMBER) "risetime") risetime)) - (ny:typecheck (not (numberp falltime)) - (ny:error "FOLLOW" 4 '((NUMBER) "stepsize") falltime)) - (ny:typecheck (not (numberp lookahead)) - (ny:error "FOLLOW" 5 '((NUMBER) "lookahead") lookahead)) - ;; use 10000s as "infinite" -- that's about 2^30 samples at 96K - (setf lookahead (round (* lookahead (snd-srate sound)))) - (extract (/ lookahead (snd-srate sound)) 10000 - (snd-follow sound floor risetime falltime lookahead))) - - -;; PHASE VOCODER -(defun phasevocoder (s map &optional (fftsize -1) (hopsize -1) (mode 0)) - (multichan-expand "PHASEVOCODER" #'snd-phasevocoder - '(((SOUND) nil) ((SOUND) "map") ((INTEGER) "fftsize") - ((INTEGER) "hopsize") ((INTEGER) "mode")) - s map fftsize hopsize mode)) - - -;; PV-TIME-PITCH -;; PV-TIME-PITCH -- control time stretch and transposition -;; -;; stretchfn maps from input time to output time -;; pitchfn maps from input time to transposition factor (2 means octave up) -(defun pv-time-pitch (input stretchfn pitchfn dur &optional - (fftsize 2048) (hopsize nil) (mode 0)) - (multichan-expand "PV-TIME-PITCH" #'nyq:pv-time-pitch - '(((SOUND) "input") ((SOUND) "stretchfn") ((SOUND) "pitchfn") - ((NUMBER) "dur") ((INTEGER) "fftsize") ((INT-OR-NULL) "hopsize") - ((INTEGER) "mode")) - input stretchfn pitchfn dur fftsize hopsize mode)) - -(defun nyq:pv-time-pitch (input stretchfn pitchfn dur fftsize hopsize mode) - (let (wrate u v w vinv) - (if (null hopsize) (setf hopsize (/ fftsize 8))) - (setf wrate (/ 3000 dur)) - (setf vinv (integrate (prod stretchfn pitchfn))) - (setf v (snd-inverse vinv (local-to-global 0) wrate)) - (setf w (integrate (snd-recip (snd-compose pitchfn v)))) - (sound-warp w (phasevocoder input v fftsize hopsize mode) wrate))) - diff --git a/Release/nyquist/envelopes.lsp b/Release/nyquist/envelopes.lsp deleted file mode 100644 index 18e6a6f6524e4d76aa3fadfe84a521ed6389cd24..0000000000000000000000000000000000000000 --- a/Release/nyquist/envelopes.lsp +++ /dev/null @@ -1,163 +0,0 @@ -;; envelopes.lsp -- support functions for envelope editor in NyquistIDE - -#| In Nyquist, editable envelopes are saved as one entry in the workspace -named *envelopes*. The entry is an association list where each element -looks like this: - -(name type parameters... ) - -where name is a symbol, e.g. MY-ENVELOPE-1, - type is a function name, e.g. PWL, PWLV, PWE, etc., and - parameters are breakpoint data, e.g. 0.1 1 0.2 0.5 1 - -Example of two envelopes named FOO and BAR: - -((FOO PWL 0.1 1 1) (BAR PWE 0.2 1 1)) - -To convert envelope data into functions, call (MAKE-ENV-FUNCTIONS). -This function should be on the workspace's list of functions to call. -(See ADD-ACTION-TO-WORKSPACE in Nyquist Manual.) - -When the NyquistIDE wants to get the envelope data from the workspace, it -should call (GET-ENV-DATA), which will dump formatted data to Nyquist's -standard output as follows: - -get-env-data: begin -name (type parameters...) newline -name (type parameters...) newline -... -get-env-data: end - -When the IDE wants to save a definition, it should call -(DEFINE-ENV 'NAME 'EXPRESSION) - -To delete a definition, call: -(DELETE-ENV 'NAME) - -Envelope data will be loaded when the editor window is opened and saved -whenever the user issues a "save" command. If the user switches envelopes -without saving, there is a prompt to save or ignore. - -The user will also be prompted to save when the editor window is closed -or when Nyquist is exited. - -Saving the workspace automatically is something that Nyquist should do -(or prompt the user to do) when it exits. - -|# - -;; WORKSPACE -- the workspace is just a set of variables, typically -;; with scores as values. These are stored in the file workspace.lsp -;; so that you can work on some data and then store it for use later. - -(cond ((not (boundp '*workspace*)) - (setf *workspace* nil))) -(cond ((not (boundp '*workspace-actions*)) - (setf *workspace-actions* nil))) -;; one of the variables in the workspace is *envelopes* -(cond ((not (boundp '*envelopes*)) - (setf *envelopes* nil))) - -;; DESCRIBE -- add a description to a global variable -;; -(defun describe (symbol &optional description) - (add-to-workspace symbol) - (cond (description - (putprop symbol description 'description)) - (t - (get symbol 'description)))) - -;; ADD-TO-WORKSPACE -- add a global symbol to workspace -;; -(defun add-to-workspace (symbol) - (cond ((not (symbolp symbol)) - (format t "add-to-workspace expects a (quoted) symbol~%")) - ((not (member symbol *workspace*)) - (push symbol *workspace*)))) - - -;; ADD-ACTION-TO-WORKSPACE -- call function when workspace is loaded -;; -(defun add-action-to-workspace (symbol) - (cond ((not (symbolp symbol)) - (format t "add-action-to-workspace expects a (quoted) symbol~%")) - ((not (member symbol *workspace-actions*)) - (push symbol *workspace-actions*)))) - -;; SAVE-WORKSPACE -- write data to file -;; -(defun save-workspace () - (let (val (outf (open "workspace.lsp" :direction :output))) - (dolist (sym *workspace*) - (format outf "(add-to-workspace '~A)~%" sym) - (cond ((get sym 'description) - (format outf "(putprop '~A \"~A\" 'description)~%" - sym (get sym 'description)))) - (format outf "(setf ~A '" sym) - (setf val (symbol-value sym)) - (cond ((listp val) - (format outf "(~%") - (dolist (elem val) - (format outf " ~A~%" elem)) - (format outf " ))~%~%")) - (t - (format outf "~A)~%~%" val)))) - (dolist (sym *workspace-actions*) ;; call hooks after reading data - (format outf "(add-action-to-workspace '~A)~%" sym) - (format outf "(if (fboundp '~A) (~A))~%" sym sym)) - (format outf "(princ \"workspace loaded\\n\")~%") - (close outf) - (princ "workspace saved\n") - nil)) - - -;; DEFINE-ENV -- save the env data and make corresponding function -;; -(defun define-env (name expression) - (delete-env name) - (push (cons name expression) *envelopes*) - (make-env-function name expression) - ; make sure envelopes are redefined when workspace is loaded - (add-to-workspace '*envelopes*) ; so *envelopes* will be saved - (describe '*envelopes* "data for envelope editor in NyquistIDE") - (add-action-to-workspace 'make-env-functions) - nil) - - -;; DELETE-ENV -- delete an envelope definition from workspace -;; -;; note that this will not undefine the corresponding envelope function -;; -(defun delete-env (name) - (setf *envelopes* - (remove name *envelopes* - :test #'(lambda (key item) (eql key (car item)))))) - - -;; MAKE-ENV-FUNCTION -- convert data to a defined function -;; -(defun make-env-function (name expression) - (setf (symbol-function name) - (eval (list 'lambda '() expression)))) - - -;; MAKE-ENV-FUNCTIONS -- convert data to defined functions -;; -(defun make-env-functions () - (let (name type parameters) - (dolist (env *envelopes*) - (setf name (car env)) - (setf type (cadr env)) - (setf parameters (cddr env)) - (make-env-function name (cons type parameters))))) - - -;; GET-ENV-DATA -- print env data for IDE -;; -(defun get-env-data () - (princ "get-env-data: begin\n") - (dolist (env *envelopes*) - (format t "~A ~A~%" (car env) (cdr env))) - (princ "get-env-data: end\n") - nil) - diff --git a/Release/nyquist/equalizer.lsp b/Release/nyquist/equalizer.lsp deleted file mode 100644 index 12ff4873c8623860c2e1a89ee8708c0eb776e9f4..0000000000000000000000000000000000000000 --- a/Release/nyquist/equalizer.lsp +++ /dev/null @@ -1,75 +0,0 @@ -;; equalizer.lsp -- support functions for equalizer editor in jNyqIDE - -#| This is modeled after envelopes.lsp, which details how envelope data is -exchanged between Nyquist and jNyqIDE. - -The jNyqIDE code needs some work to make it look like the envelope -editor (which also needs work, but that's another matter). For consistency, -both should support named envelopes and equalizers. - -However, for now, we have equalizers numbered from 0 to 9. The format for -exchange will be: - -get-eq-data: begin -name parameters newline -name parameters newline -... -get-eq-data: end - -and when the IDE wants to save a definition, it should call -(DEFINE-EQ 'NAME 'PARAMETER-LIST) - -|# - -(cond ((not (boundp '*equalizers*)) - (setf *equalizers* nil))) - -;; DEFINE-EQ -- save the eq data and make corresponding function -;; -(defun define-eq (name expression) - (setf *equalizers* (remove name *equalizers* - :test #'(lambda (key item) (eql key (car item))))) - (push (list name expression) *equalizers*) - (make-eq-function name expression) - ; make sure equalizers are redefined when workspace is loaded - (add-to-workspace '*equalizers*) - (describe '*equalizers* "data for equalizers in jNyqIDE") - (add-action-to-workspace 'make-eq-functions) - nil) - - -;; MAKE-EQ-FUNCTION -- convert data to a defined function -;; -(defun make-eq-function (name parameters) - (cond ((numberp name) - (setf name (intern (format nil "EQ-~A" name))))) - (if (not (boundp '*grapheq-loaded*)) (load "grapheq.lsp")) - (setf (symbol-function name) - (eval `(lambda (s) (nband-range s ',parameters 60 14000))))) - - -;; MAKE-EQ-FUNCTIONS -- convert data to defined functions -;; -(defun make-eq-functions () - (let (name type parameters) - (dolist (eq *equalizers*) - (setf name (car eq)) - (setf parameters (second parameters)) - (make-eq-function name parameters)))) - - -;; GET-EQ-DATA -- print env data for IDE -;; -(defun get-eq-data () - (let (parameters) - (princ "get-eq-data: begin\n") - (dolist (env *equalizers*) - (format t "~A" (car env)) - (setf parameters (second env)) - (dotimes (i (length parameters)) - (format t " ~A" (aref parameters i))) - (format t "~%")) - (princ "get-eq-data: end\n") - nil)) - - diff --git a/Release/nyquist/evalenv.lsp b/Release/nyquist/evalenv.lsp deleted file mode 100644 index da8ce76896465cd4ff3a928df4992449675ebef7..0000000000000000000000000000000000000000 --- a/Release/nyquist/evalenv.lsp +++ /dev/null @@ -1,36 +0,0 @@ -;; -;; The EVAL function in the original XLISP evaluated in the current lexical -;; context. This was changed to evaluate in the NIL (global) context to -;; match Common Lisp. But this created a problem: how do you EVAL an -;; expression in the current lexical context? -;; -;; The answer is you can use the evalhook facility. The evalhook function -;; will evaluate an expression using an environment given to it as an -;; argument. But then the problem is "how do you get the current -;; environment?" Well the getenv macro, below obtains the environment by -;; using an *evalhook* form. -;; -;; The following two macros do the job. Insteading of executing (eval <expr>) -;; just execute (eval-env <expr>). If you want, you can dispense with the -;; macros and execute: -;; -;;(evalhook <expr> nil nil (let ((*evalhook* (lambda (x env) env))) -;; (eval nil))) -;; -;; Tom Almy 10/91 -;; - -(defmacro getenv () - '(progv '(*evalhook*) - (list #'(lambda (exp env) env)) - (eval nil))) - -; this didn't work, may be for a later (Almy) version of xlisp? -;(defmacro getenv () -; '(let ((*evalhook* (lambda (x env) env))) -; (eval nil))) ; hook function evaluates by returning - ; environment - -(defmacro eval-env (arg) ; evaluate in current environment - `(evalhook ,arg nil nil (getenv))) - diff --git a/Release/nyquist/fileio.lsp b/Release/nyquist/fileio.lsp deleted file mode 100644 index f09914b84743cafc765416f66058382146a827dd..0000000000000000000000000000000000000000 --- a/Release/nyquist/fileio.lsp +++ /dev/null @@ -1,417 +0,0 @@ -;; fileio.lsp - -;; if *default-sf-dir* undefined, set it to user's tmp directory -;; -(cond ((not (boundp '*default-sf-dir*)) - ;; it would be nice to use get-temp-path, but when running - ;; the Java-based IDE, Nyquist does not get environment - ;; variables to tell TMP or TEMP or USERPROFILE - ;; We want to avoid the current directory because it may - ;; be read-only. Search for some likely paths... - ;; Note that since these paths don't work for Unix or OS X, - ;; they will not be used, so no system-dependent code is - ;; needed - (let ((current (setdir "."))) - (setf *default-sf-dir* - (or (setdir "c:\\tmp\\" nil) - (setdir "c:\\temp\\" nil) - (setdir "d:\\tmp\\" nil) - (setdir "d:\\temp\\" nil) - (setdir "e:\\tmp\\" nil) - (setdir "e:\\temp\\" nil) - (get-temp-path))) - (format t "Set *default-sf-dir* to \"~A\" in fileio.lsp~%" - *default-sf-dir*) - (setdir current)))) - -;; if the steps above fail, then *default-sf-dir* might be "" (especially -;; on windows), and the current directory could be read-only on Vista and -;; Windows 7. Therefore, the Nyquist IDE will subsequently call -;; suggest-default-sf-dir with Java's idea of a valid temp directory. -;; If *default-sf-dir* is the empty string (""), this will set the variable: -(defun suggest-default-sf-dir (path) - (cond ((equal *default-sf-dir* "") (setf *default-sf-dir* path)))) - -;; s-save -- saves a file -(setf *in-s-save* nil) -(setf NY:ALL 576460752303423488) ; constant for maxlen == 1 << 59 -;; note that at 16-bytes-per-frame, this could generate a file byte offset -;; that overflows an int64_t. Is this big enough? Time will tell. -;; What if Nyquist is compiled for 32-bit machines and FIXNUM is 32-bits? -;; if we don't have 64-bit ints, use 0x7f000000, which is about 10M less -;; than the maximum signed 32-bit int, giving a lot of "headroom" but still -;; over 2 billion, or about 13.4 hours at 44.1KHz -(if (/= 10000000000 (* 100000 100000)) - (setf NY:ALL 2130706432)) - - -;; S-SAVE combines optional and keyword parameters, but this is a really bad -;; idea because keywords and values are used as optional parameters until -;; all the optional parameters are used up. Thus if you leave out filename -;; and progress, but you provide :endian T, then filename becomes :endian and -;; progress becomes T. AARRGG!! -;; I should have required filename and made everything else keyword, but -;; rather than breaking compatibility, I'm using &rest to grab everything, -;; parse the parameters for keywords (giving them priority over optional -;; parameters, and filling in optional parameters as they are encountered. -;; -(defmacro s-save (expression &rest parameters) - (prog (parm (format *default-sf-format*) - (mode *default-sf-mode*) - (bits *default-sf-bits*) - ;; endian can be nil, :big, or :little - endian play optionals maxlen filename progress swap) - loop ;; until all parameters are used - (cond ((setf parm (car parameters)) - (setf parameters (cdr parameters)) - (case parm - (:format (setf format (car parameters) - parameters (cdr parameters))) - (:mode (setf mode (car parameters) - parameters (cdr parameters))) - (:bits (setf bits (car parameters) - parameters (cdr parameters))) - (:endian (setf endian (car parameters) - parameters (cdr parameters))) - (:play (setf play (car parameters) - parameters (cdr parameters))) - (t (setf optionals (cons parm optionals)))) - (go loop))) - (cond ((> (length optionals) 3) - (error "S-SAVE got extra parameter(s)"))) - (cond ((< (length optionals) 1) ;; need maxlen - (setf optionals (list ny:all)))) - (cond ((< (length optionals) 2) ;; need filename - (setf optionals (cons nil optionals)))) - (cond ((< (length optionals) 3) ;; need progress - (setf optionals (cons 0 optionals)))) - (setf progress (first optionals) ;; note that optionals are in reverse order - filename (second optionals) - maxlen (third optionals)) - (cond (*in-s-save* - (error "Recursive call to S-SAVE (or maybe PLAY) detected!"))) - - ;; finally, we have all the parameters and we can call snd-save - (return - `(let ((ny:fname ,filename) (ny:swap 0) (ny:endian ,endian) - (ny:play ,play) - ny:max-sample) ; return value - (progv '(*in-s-save*) '(t) - (if (null ny:fname) - (setf ny:fname *default-sound-file*)) - - (cond ((equal ny:fname "") - (cond ((not ,play) - (format t "S-SAVE: no file to write! ~ - play option is off!\n")))) - (t - (setf ny:fname (soundfilename ny:fname)) - (format t "Saving sound file to ~A~%" ny:fname))) - - (cond ((eq ny:endian :big) - (setf ny:swap (if (bigendianp) 0 1))) - ((eq ny:endian :little) - (setf ny:swap (if (bigendianp) 1 0)))) - - ; print device info the first time sound is played - (cond (ny:play - (cond ((not (boundp '*snd-list-devices*)) - (setf *snd-list-devices* t))))) ; one-time show - (setf max-sample - (snd-save ',expression ,maxlen ny:fname ,format - ,mode ,bits ny:swap ny:play ,progress)) - ; more information if *snd-list-devices* was unbound: - (cond (ny:play - (cond (*snd-list-devices* - (format t "\nSet *snd-lfist-devices* = t \n ~ - and call play to see device list again.\n~ - Set *snd-device* to a fixnum to select an output device\n ~ - or set *snd-device* to a substring of a device name\n ~ - to select the first device containing the substring.\n"))) - (setf *snd-list-devices* nil))) ; normally nil - max-sample))))) - - -;; MULTICHANNEL-MAX -- find peak over all channels -;; -(defun multichannel-max (snd samples) - (cond ((soundp snd) - (snd-max snd samples)) - ((arrayp snd) ;; assume it is multichannel sound - (let ((peak 0.0) (chans (length snd))) - (dotimes (i chans) - (setf peak (max peak (snd-max (aref snd i) (/ samples chans))))) - peak)) - (t (error "unexpected value in multichannel-max" snd)))) - - - -;; AUTONORM -- look ahead to find peak and normalize sound to 80% -;; -(defun autonorm (snd) - (let (peak) - (cond (*autonormflag* - (cond ((and (not (soundp snd)) - (not (eq (type-of snd) 'ARRAY))) - (error "AUTONORM (or PLAY?) got unexpected value" snd)) - ((eq *autonorm-type* 'previous) - (scale *autonorm* snd)) - ((eq *autonorm-type* 'lookahead) - (setf peak (multichannel-max snd *autonorm-max-samples*)) - (setf peak (max 0.001 peak)) - (setf *autonorm* (/ *autonorm-target* peak)) - (scale *autonorm* snd)) - (t - (error "unknown *autonorm-type*")))) - (t snd)))) - - -(init-global *clipping-threshold* (/ 127.0 128.0)) - -(defmacro s-save-autonorm (expression &rest arglist) - `(let ((peak (s-save (autonorm ,expression) ,@arglist))) - (when (and *clipping-error* (> peak *clipping-threshold*)) - (format t "s-save-autonorm peak ~A from ~A~%" peak ,expression) - (error "clipping")) - (autonorm-update peak))) - -;; If the amplitude exceeds *clipping-threshold*, an error will -;; be raised if *clipping-error* is set. -;; -(init-global *clipping-error* nil) - -;; The "AutoNorm" facility: when you play something, the Nyquist play -;; command will automatically compute what normalization factor you -;; should have used. If you play the same thing again, the normalization -;; factor is automatically applied. -;; -;; Call AUTONORM-OFF to turn off this feature, and AUTONORM-ON to turn -;; it back on. -;; -;; *autonorm-target* is the peak value we're aiming for (it's set below 1 -;; so allow the next signal to get slightly louder without clipping) -;; -(init-global *autonorm-target* 0.9) -;; -;; *autonorm-type* selects the autonorm algorithm to use -;; 'previous means normalize according to the last computed sound -;; 'precompute means precompute *autonorm-max-samples* samples in -;; memory and normalize according to the peak -;; -(init-global *autonorm-type* 'lookahead) -(init-global *autonorm-max-samples* 1000000) ; default is 4MB buffer - -;; -(defun autonorm-on () - (setf *autonorm* 1.0) - (setf *autonorm-previous-peak* 1.0) - (setf *autonormflag* t) - (format t "AutoNorm feature is on.~%")) - -(if (not (boundp '*autonormflag*)) (autonorm-on)) - -(defun autonorm-off () - (setf *autonormflag* nil) - (setf *autonorm* 1.0) - (format t "AutoNorm feature is off.~%")) - -(defun explain-why-autonorm-failed () - (format t "~A~A~A~A~A~A" - " *autonorm-type* is LOOKAHEAD and your sound got\n" - " louder after the lookahead period, resulting in\n" - " too large a scale factor and clipping. Consider\n" - " setting *autonorm-type* to 'PREVIOUS. Alternatively,\n" - " try turning off autonorm, e.g. \"exec autonorm-off()\"\n" - " or in Lisp mode, (autonorm-off), and scale your sound\n" - " as follows.\n")) - - -;; AUTONORM-UPDATE -- called with true peak to report and prepare -;; -;; after saving/playing a file, we have the true peak. This along -;; with the autonorm state is printed in a summary and the autonorm -;; state is updated for next time. -;; -;; There are currently two types: PREVIOUS and LOOKAHEAD -;; With PREVIOUS: -;; compute the true peak and print the before and after peak -;; along with the scale factor to be used next time -;; With LOOKAHEAD: -;; compute the true peak and print the before and after peak -;; along with the "suggested scale factor" that would achieve -;; the *autonorm-target* -;; -(defun autonorm-update (peak) - (cond ((> peak 1.0) - (format t "*** CLIPPING DETECTED! ***~%"))) - (cond ((and *autonormflag* (> peak 0.0)) - (setf *autonorm-previous-peak* (/ peak *autonorm*)) - (setf *autonorm* (/ *autonorm-target* *autonorm-previous-peak*)) - (format t "AutoNorm: peak was ~A,~%" *autonorm-previous-peak*) - (format t " peak after normalization was ~A,~%" peak) - (cond ((eq *autonorm-type* 'PREVIOUS) - (cond ((zerop *autonorm*) - (setf *autonorm* 1.0))) - (format t " new normalization factor is ~A~%" *autonorm*)) - ((eq *autonorm-type* 'LOOKAHEAD) - (cond ((> peak 1.0) - (explain-why-autonorm-failed))) - (format t " suggested manual normalization factor is ~A~%" - *autonorm*)) - (t - (format t - " unexpected value for *autonorm-type*, reset to LOOKAHEAD\n") - (setf *autonorm-type* 'LOOKAHEAD)))) - (t - (format t "Peak was ~A,~%" peak) - (cond ((> peak 0.0) - (format t " suggested normalization factor is ~A~%" - (/ *autonorm-target* peak)))))) - peak - ) - - -;; s-read -- reads a file -(defun s-read (filename &key (time-offset 0) (srate *sound-srate*) - (dur 10e20) (nchans 1) (format *default-sf-format*) - (mode *default-sf-mode*) (bits *default-sf-bits*) (endian NIL)) - (let ((swap 0)) - (cond ((eq endian :big) - (setf swap (if (bigendianp) 0 1))) - ((eq endian :little) - (setf swap (if (bigendianp) 1 0)))) - (if (minusp dur) (error "s-read :dur is negative" dur)) - (snd-read (soundfilename filename) time-offset - (local-to-global 0) format nchans mode bits swap srate - dur))) - - -;; SF-INFO -- print sound file info -;; -(defun sf-info (filename) - (let (s format channels mode bits swap srate dur flags) - (format t "~A:~%" (soundfilename filename)) - (setf s (s-read filename)) - (setf format (snd-read-format *rslt*)) - (setf channels (snd-read-channels *rslt*)) - (setf mode (snd-read-mode *rslt*)) - (setf bits (snd-read-bits *rslt*)) - ; (setf swap (snd-read-swap *rslt*)) - (setf srate (snd-read-srate *rslt*)) - (setf dur (snd-read-dur *rslt*)) - (setf flags (snd-read-flags *rslt*)) - (format t "Format: ~A~%" - (nth format '("none" "AIFF" "IRCAM" "NeXT" "Wave" "PAF" "SVX" - "NIST" "VOC" "W64" "MAT4" "Mat5" "PVF" "XI" "HTK" - "SDS" "AVR" "SD2" "FLAC" "CAF"))) - (cond ((setp (logand flags snd-head-channels)) - (format t "Channels: ~A~%" channels))) - (cond ((setp (logand flags snd-head-mode)) - (format t "Mode: ~A~%" - (nth mode '("ADPCM" "PCM" "uLaw" "aLaw" "Float" "UPCM" - "unknown" "double" "GSM610" "DWVW" "DPCM" - "msadpcm"))))) - (cond ((setp (logand flags snd-head-bits)) - (format t "Bits/Sample: ~A~%" bits))) - (cond ((setp (logand flags snd-head-srate)) - (format t "SampleRate: ~A~%" srate))) - (cond ((setp (logand flags snd-head-dur)) - (format t "Duration: ~A~%" dur))) - )) - -;; SETP -- tests whether a bit is set (non-zero) -; -(defun setp (bits) (not (zerop bits))) - -;; IS-FILE-SEPARATOR -- is this a file path separation character, e.g. "/"? -;; -(defun is-file-separator (c) - (or (eq c *file-separator*) - (and (eq *file-separator* #\\) ;; if this is windows (indicated by "\") - (eq c #\/)))) ;; then "/" is also a file separator - -;; SOUNDFILENAME -- add default directory to name to get filename -;; -(defun soundfilename (filename) - (cond ((= 0 (length filename)) - (break "filename must be at least one character long" filename)) - ((full-name-p filename)) - (t - ; if sf-dir nonempty and does not end with filename separator, - ; append one - (cond ((and (< 0 (length *default-sf-dir*)) - (not (is-file-separator - (char *default-sf-dir* - (1- (length *default-sf-dir*)))))) - (setf *default-sf-dir* (strcat *default-sf-dir* (string *file-separator*))) - (format t "Warning: appending \"~A\" to *default-sf-dir*~%" - *file-separator*))) - (setf filename (strcat *default-sf-dir* (string filename))))) - ;; now we have a file name, but it may be relative to current directory, so - ;; expand it with the current directory - (cond ((relative-path-p filename) - ;; get current working directory and build full name - (let ((path (setdir "."))) - (cond (path - (setf filename (strcat path (string *file-separator*) - (string filename)))))))) - filename) - - -(setfn snd-read-format car) -(setfn snd-read-channels cadr) -(setfn snd-read-mode caddr) -(setfn snd-read-bits cadddr) -(defun snd-read-swap (rslt) (car (cddddr rslt))) -(defun snd-read-srate (rslt) (cadr (cddddr rslt))) -(defun snd-read-dur (rslt) (caddr (cddddr rslt))) -(defun snd-read-flags (rslt) (cadddr (cddddr rslt))) - -;; round is tricky because truncate rounds toward zero as does C -;; in other words, rounding is down for positive numbers and up -;; for negative numbers. You can convert rounding up to rounding -;; down by subtracting one, but this fails on the integers, so -;; we need a special test if (- x 0.5) is an integer -(defun round (x) - (cond ((> x 0) (truncate (+ x 0.5))) - ((= (- x 0.5) (truncate (- x 0.5))) (truncate x)) - (t (truncate (- x 0.5))))) - -;; change defaults for PLAY macro: -(init-global *soundenable* t) -(defun sound-on () (setf *soundenable* t)) -(defun sound-off () (setf *soundenable* nil)) - -(defun coterm (snd1 snd2) - (multichan-expand #'snd-coterm snd1 snd2)) - -(defmacro s-add-to (expr maxlen filename - &optional (time-offset 0.0) (progress 0)) - `(let ((ny:fname (soundfilename ,filename)) - ny:peak ny:input (ny:offset ,time-offset)) - (format t "Adding sound to ~A at offset ~A~%" - ny:fname ,time-offset) - (setf ny:peak (snd-overwrite '(let ((ny:addend ,expr)) - (sum (coterm - (s-read ny:fname - :time-offset ny:offset) - ny:addend) - ny:addend)) - ,maxlen ny:fname ny:offset ,progress)) - (format t "Duration written: ~A~%" (car *rslt*)) - ny:peak)) - - -(defmacro s-overwrite (expr maxlen filename - &optional (time-offset 0.0) (progress 0)) - `(let ((ny:fname (soundfilename ,filename)) - (ny:peak 0.0) - ny:input ny:rslt (ny:offset ,time-offset)) - (format t "Overwriting ~A at offset ~A~%" ny:fname ny:offset) - (setf ny:peak (snd-overwrite `,expr ,maxlen ny:fname ny:offset ,progress)) - (format t "Duration written: ~A~%" (car *rslt*)) - ny:peak)) - - - - diff --git a/Release/nyquist/init.lsp b/Release/nyquist/init.lsp deleted file mode 100644 index 102d0ab82fc8f3420b0aac65c3a5160212c29d36..0000000000000000000000000000000000000000 --- a/Release/nyquist/init.lsp +++ /dev/null @@ -1,88 +0,0 @@ -; init.lsp -- default Nyquist startup file - -(setf *breakenable* t) -(load "nyinit.lsp" :verbose nil) - -; add your customizations here: -; e.g. (setf *default-sf-dir* "...") - -; (load "test.lsp") - - - -;; "_" (UNDERSCORE) - translation function -;; -;; Third party plug-ins are not translated by gettext in Audacity, but may include a -;; list of translations named *locale*. The format of *locale* must be: -;; (LIST (language-list) [(language-list) ...]) -;; Each language-list is an a-list in the form: -;; ("cc" ((list "string" "translated-string") [(list "string" "translated-string") ...])) -;; where "cc" is the quoted country code. -;; -(setfn underscore _) -;; -(defun _(txt &aux newtxt) - (when (boundp '*locale*) - (when (not (listp *locale*)) - (error "bad argument type" *locale*)) - (let* ((cc (get '*audacity* 'language)) - (translations (second (assoc cc *locale* :test 'string-equal)))) - (if translations - (let ((translation (second (assoc txt translations :test 'string=)))) - (if translation - (if (stringp translation) - (setf newtxt translation) - (error "bad argument type" translation)) - (format t "No ~s translation of ~s.~%" cc txt))) - (progn - (setf *locale* '*unbound*) - (format t "No ~s translations.~%" cc))))) - (if newtxt newtxt (underscore txt))) - - -;;; Some helpers for parsing strings returned by (aud-do "GetInfo: ... - -(defun eval-string (string) - ;;; Evaluate a string as a LISP expression. - ;;; If 'string' is not a valid LISP expression, the behaviour is undefined. - (eval (read (make-string-input-stream string)))) - -(defmacro quote-string (string) - ;;; Prepend a single quote to a string - `(setf ,string (format nil "\'~a" ,string))) - -(defun aud-get-info (str) - ;;; Return "GetInfo: type=type" as Lisp list, or throw error - ;;; Audacity 2.3.0 does not fail if type is not recognised, it - ;;; falls back to a default, so test for valid types. - ;;; 'Commands+' is not supported in Audacity 2.3.0 - (let (type - info - (types '("Commands" "Menus" "Preferences" - "Tracks" "Clips" "Envelopes" "Labels" "Boxes"))) - ;Case insensitive search, then set 'type' with correct case string, or NIL. - (setf type (first (member str types :test 'string-equal))) - (if (not type) - (error (format nil "bad argument '~a' in (aud-get-info ~a)" str str))) - (setf info (aud-do (format nil "GetInfo: type=~a format=LISP" type))) - (if (not (last info)) - (error (format nil "(aud-get-info ~a) failed.~%" str))) - (let* ((info-string (first info)) - (sanitized "")) - ;; Escape backslashes - (dotimes (i (length info-string)) - (setf ch (subseq info-string i (1+ i))) - (if (string= ch "\\") - (string-append sanitized "\\\\") - (string-append sanitized ch))) - (eval-string (quote-string sanitized))))) - - -;;; *NYQ-PATH* is not required as path to Nyquist .lsp files -;;; is already defined (but not previously documented) as *runtime-path* -;;(setf *NYQ-PATH* (current-path)) - -;;; Load wrapper functions for aud-do commands. -;;; If commented out, "aud-do-support.lsp" may be loaded by a plug-in. -;;; Example: (lisp-loader (strcat *runtime-path* "aud-do-support.lsp")) -(load "aud-do-support.lsp" :verbose nil) diff --git a/Release/nyquist/misc.lsp b/Release/nyquist/misc.lsp deleted file mode 100644 index c81726ca80c009373b40f2df34ff59ba540da5d8..0000000000000000000000000000000000000000 --- a/Release/nyquist/misc.lsp +++ /dev/null @@ -1,235 +0,0 @@ -;## misc.lsp -- a collection of useful support functions - -;; Garbage collection "improvement" -- XLISP will GC without allocation -;; as long as it does not run out of cells. This can make it very slow -;; since GC does work proportional to the heap size. If there were -;; always at least, say, 1/3 of the heap free after GC, then allocating -;; cells would be more-or-less a constant time operation (amortized). -;; -;; So, after GC, we'll expand until we have 1/3 of the heap free. -;; -(defun ny:gc-hook (heap-size free-cells) - (cond ((< (* free-cells 2) heap-size) ;; free cells is < 1/3 heap - ;; expand. Each expansion unit is 2000 cons cells - (let* ((how-many-not-free (- heap-size free-cells)) - (should-be-free (/ how-many-not-free 2)) - (how-many-more (- should-be-free free-cells)) - (expand-amount (/ how-many-more 2000))) - (cond ((> expand-amount 0) - (if *gc-flag* - (format t - "[ny:gc-hook allocating ~A more cells] " - (* expand-amount 2000))) - (expand expand-amount))))))) - -(setf *gc-hook* 'ny:gc-hook) - - -; set global if not already set -; -(defmacro init-global (symb expr) - `(if (boundp ',symb) ,symb (setf ,symb ,expr))) - -; controlling breaks and tracebacks: -; XLISP and SAL behave differently, so there are four(!) flags: -; *sal-traceback* -- print SAL traceback on error in SAL mode -; Typically you want this on always. -; *sal-break* -- allow break (to XLISP prompt) on error when in SAL mode -; (overrides *sal-traceback*) Typically, you do not want -; this unless you need to see exactly where an error happened -; or the bug is in XLISP source code called from SAL. -; *xlisp-break* -- allow break on error when in XLISP mode -; Typically, you want this on. -; *xlisp-traceback* -- print XLISP traceback on error in XLISP mode -; Typically, you do not want this because the full -; stack can be long and tedious. Also allow XLISP -; traceback in SAL mode if *sal-break* is true. - -(setf *sal-mode* nil) - -(setf *sal-traceback* t - *sal-break* nil - *xlisp-break* t - *xlisp-traceback* nil) - -(defun sal-tracenable (flag) (setf *sal-traceback* flag)) -(defun sal-breakenable (flag) - (setf *sal-break* flag) - (if *sal-mode* (setf *breakenable* flag))) -(defun xlisp-breakenable (flag) - (setf *xlisp-break* flag) - (if (not *sal-mode*) (setf *breakenable* flag))) -(defun xlisp-tracenable (flag) - (setf *xlisp-traceback* flag) - (if flag (setf *xlisp-break* t)) - (cond ((not *sal-mode*) - (if flag (setf *breakenable* t)) - (setf *tracenable* flag)))) - - -; enable or disable breaks -(defun bkon () (xlisp-breakenable t)) -(defun bkoff () (xlisp-breakenable nil)) - - -;; (grindef 'name) - pretty print a function -;; -(defun grindef (e) (pprint (get-lambda-expression (symbol-function e)))) - -;; (args 'name) - print function and its formal arguments -;; -(defun args (e) - (pprint (cons e (second (get-lambda-expression (symbol-function e)))))) - -;; (incf <place>), (decf <place>) - add/sub 1 to/from variable -;; -(defmacro incf (symbol) `(setf ,symbol (1+ ,symbol))) -(defmacro decf (symbol) `(setf ,symbol (1- ,symbol))) - - -;; (push val <place>) - cons val to list -;; -(defmacro push (val lis) `(setf ,lis (cons ,val ,lis))) -(defmacro pop (lis) `(prog1 (car ,lis) (setf ,lis (cdr ,lis)))) - -;; include this to use RBD's XLISP profiling hooks -;;(load "/afs/andrew/usr/rbd/lib/xlisp/profile.lsp") - -;(cond ((boundp 'application-file-name) -; (load application-file-name))) - - -(defun get-input-file-name () - (let (fname) - (format t "Input file name: ") - (setf fname (read-line)) - (cond ((equal fname "") (get-input-file-name)) - (t fname)))) - - -(defun open-output-file () - (let (fname) - (format t "Output file name: ") - (setf fname (read-line)) - (cond ((equal fname "") t) - (t (open fname :direction :output))))) - - -(defmacro while (cond &rest stmts) - `(prog () loop (if ,cond () (return)) ,@stmts (go loop))) - - -; when parens/quotes don't match, try this -; -(defun file-sexprs () - (let ((fin (open (get-input-file-name))) - inp) - (while (setf inp (read fin)) (print inp)))) - -;; get path for currently loading file (if any) -;; -(defun current-path () - (let (fullpath n) - (setf n -1) - (cond (*loadingfiles* - (setf fullpath (car *loadingfiles*)) - (dotimes (i (length fullpath)) - ;; search for "/" (and on windows, also "\") in path: - (cond ((or (equal (char fullpath i) *file-separator*) - (equal (char fullpath i) #\/)) - (setf n i)))) - ;; trim off filename (after last separator char in path - (setf fullpath (subseq fullpath 0 (1+ n))) - -;; REMOVED SUPPORT FOR MAC OS-9 AND BELOW -RBD - ;; if this is a Mac, use ':' in place of empty path -;; (cond ((and (equal fullpath "") -;; (equal *file-separator* #\:)) -;; (setf fullpath ":"))) -;; END MAC OS-9 CODE - - ;; Here's an interesting problem: fullpath is now the path - ;; specified to LOAD, but it may be relative to the current - ;; directory. What if we want to load a sound file from the - ;; current directory? It seems that S-READ gives priority to - ;; the *DEFAULT-SF-DIR*, so it will follow fullpath STARTING - ;; FROM *DEFAULT-SF-DIR*. To fix this, we need to make sure - ;; that fullpath is either an absolute path or starts with - ;; and explicit ./ which tells s-read to look in the current - ;; directory. - (cond ((> (length fullpath) 0) - (cond ((full-name-p fullpath)) - (t ; not absolute, make it explicitly relative - (setf fullpath (strcat "./" fullpath))))) - (t (setf fullpath "./"))) ; use current directory - fullpath) - (t nil)))) - -;; real-random -- pick a random real from a range -;; -(defun real-random (from to) - (+ (* (rrandom) (- to from)) from)) - -;; power -- raise a number to some power x^y -;; -(defun power (x y) - (exp (* (log (float x)) y))) - -;; require-from -- load a file if a function is undefined -;; -;; fn-symbol -- the function defined when the file is loaded -;; file-name -- the name of file to load if fn-symbol is undefined -;; path -- if t, load from current-path; if a string, prepend string -;; to file-name; if nil, ignore it -;; -(defmacro require-from (fn-symbol file-name &optional path) - (cond ((eq path t) - (setf file-name `(strcat (current-path) ,file-name))) - (path - (setf file-name `(strcat ,path ,file-name)))) - ; (display "require-from" file-name) - `(if (fboundp (quote ,fn-symbol)) - t - ;; search for either .lsp or .sal file - (sal-load ,file-name))) - -;; COMPUTE-DEFAULT-SOUND-FILE -- construct and set *default-sound-file* -;; -;; (this is harder than it might seem because the default place for -;; sound files is in /tmp, which is shared by users, so we'd like to -;; use a user-specific name to avoid collisions) -;; -(defun compute-default-sound-file () - (let (inf user extension) - ; the reason for the user name is that if UserA creates a temp file, - ; then UserB will not be able to overwrite it. The user name is a - ; way to give each user a unique temp file name. Note that we don't - ; want each session to generate a unique name because Nyquist doesn't - ; delete the sound file at the end of the session. - (setf user (get-user)) -#| - (cond ((null user) - (format t -"Please type your user-id so that I can construct a default -sound-file name. To avoid this message in the future, add -this to your .login file: - setenv USER <your id here> -or add this to your init.lsp file: - (setf *default-sound-file* \"<your filename here>\") - (setf *default-sf-dir* \"<full pathname of desired directory here>\") - -Your id please: ") - (setf user (read)))) -|# - ; now compute the extension based on *default-sf-format* - (cond ((= *default-sf-format* snd-head-AIFF) - (setf extension ".aif")) - ((= *default-sf-format* snd-head-Wave) - (setf extension ".wav")) - (t - (setf extension ".snd"))) - (setf *default-sound-file* - (strcat (string-downcase user) "-temp" extension)) - (format t "Default sound file is ~A.~%" *default-sound-file*))) - - diff --git a/Release/nyquist/nyinit-dbg.lsp b/Release/nyquist/nyinit-dbg.lsp deleted file mode 100644 index 352844575b500bf7dd21c3cc268213467cf8bb05..0000000000000000000000000000000000000000 --- a/Release/nyquist/nyinit-dbg.lsp +++ /dev/null @@ -1,38 +0,0 @@ -(expand 5) - -(load "xlinit.lsp" :verbose NIL) -(setf *gc-flag* nil) -(load "misc.lsp" :verbose NIL) -(load "evalenv.lsp" :verbose NIL) -(load "printrec.lsp" :verbose NIL) - -(load "sndfnint.lsp" :verbose NIL) -(load "seqfnint.lsp" :verbose NIL) - -(load "velocity.lsp" :verbose NIL) ; linear-to-vel etc -(load "nyquist-dbg.lsp" :verbose NIL) -(load "compress.lsp" :verbose NIL) - -(load "system.lsp" :verbose NIL) - -(load "seqmidi.lsp" :verbose NIL) -(load "nyqmisc.lsp" :verbose NIL) -(load "stk.lsp" :verbose NIL) -(load "envelopes.lsp" :verbose NIL) -(load "equalizer.lsp" :verbose NIL) -(load "xm.lsp" :verbose NIL) -(load "sal.lsp" :verbose NIL) - -;; set to T to get ANSI headers and NIL to get antique headers -(setf *ANSI* NIL) - -;; set to T to generate tracing code, NIL to disable tracing code -(setf *WATCH* NIL) - -(format t "~%Nyquist -- A Language for Sound Synthesis and Composition~%") -(format t " Copyright (c) 1991,1992,1995,2007-2012 by Roger B. Dannenberg~%") -(format t " Version 3.10~%~%") - -;(setf *gc-flag* t) - - diff --git a/Release/nyquist/nyinit.lsp b/Release/nyquist/nyinit.lsp deleted file mode 100644 index 47b2cbdb5d8f54def8ab690a33e98e7171718680..0000000000000000000000000000000000000000 --- a/Release/nyquist/nyinit.lsp +++ /dev/null @@ -1,36 +0,0 @@ -(expand 5) - -(load "xlinit.lsp" :verbose NIL) -(setf *gc-flag* nil) -(load "misc.lsp" :verbose NIL) -;; now compute-default-sound-file is defined; needed by system.lsp ... -(load "evalenv.lsp" :verbose NIL) -(load "printrec.lsp" :verbose NIL) - -(load "sndfnint.lsp" :verbose NIL) -(load "seqfnint.lsp" :verbose NIL) - -(load "velocity.lsp" :verbose NIL) ; linear-to-vel etc -(if (not (load "system.lsp" :verbose NIL)) - (error "Nyquist could not load system.lsp - check your installation")) -;; now *file-separator* is defined, used by nyquist.lsp... -(load "nyquist.lsp" :verbose NIL) - - -(load "seqmidi.lsp" :verbose NIL) -(load "nyqmisc.lsp" :verbose NIL) -(load "stk.lsp" :verbose NIL) -(load "envelopes.lsp" :verbose NIL) -(load "equalizer.lsp" :verbose NIL) -(load "xm.lsp" :verbose NIL) -(load "sal.lsp" :verbose NIL) - - -(format t "~%Nyquist -- A Language for Sound Synthesis and Composition~%") -(format t " Copyright (c) 1991,1992,1995,2007-2020 by Roger B. Dannenberg~%") -(format t " Version 3.16~%~%") -(load "extensions.lsp" :verbose NIL) - -;(setf *gc-flag* t) - - diff --git a/Release/nyquist/nyqmisc.lsp b/Release/nyquist/nyqmisc.lsp deleted file mode 100644 index 3905d33c5ade1b4b7d9039e2ab11411ad3653612..0000000000000000000000000000000000000000 --- a/Release/nyquist/nyqmisc.lsp +++ /dev/null @@ -1,27 +0,0 @@ -;; nyqmisc.lsp -- misc functions for nyquist - -(init-global *snd-display-max-samples* 10000) -(init-global *snd-display-print-samples* 100) - - -; (snd-display sound) -- describe a sound -(defun snd-display (sound) - (let (t0 srate len extent dur samples) - (setf srate (snd-srate sound)) - (setf t0 (snd-t0 sound)) - (setf len (snd-length sound *snd-display-max-samples*)) - (cond ((= len *snd-display-max-samples*) - (setf extent (format nil ">~A" (+ t0 (* srate *snd-display-max-samples*)))) - (setf dur (format nil ">~A" (* srate *snd-display-max-samples*)))) - (t - (setf extent (cadr (snd-extent sound *snd-display-max-samples*))) - (setf dur (/ (snd-length sound *snd-display-max-samples*) srate)))) - (cond ((> len 100) - (setf samples (format nil "1st ~A samples" *snd-display-print-samples*)) - (setf nsamples *snd-display-print-samples*)) - (t - (setf samples (format nil "~A samples" len)) - (setf nsamples len))) - (format t "~A: srate ~A, t0 ~A, extent ~A, dur ~A, ~A: ~A" - sound srate t0 extent dur samples (snd-samples sound nsamples)))) - diff --git a/Release/nyquist/nyquist-plot.txt b/Release/nyquist/nyquist-plot.txt deleted file mode 100644 index 003e6e0f457b88eba3c2a3f76a9d94865521678f..0000000000000000000000000000000000000000 --- a/Release/nyquist/nyquist-plot.txt +++ /dev/null @@ -1,3 +0,0 @@ -set nokey -plot "points.dat" with lines - diff --git a/Release/nyquist/nyquist.lsp b/Release/nyquist/nyquist.lsp deleted file mode 100644 index dcd30c35969a163292b93020a6754ffe0125ff99..0000000000000000000000000000000000000000 --- a/Release/nyquist/nyquist.lsp +++ /dev/null @@ -1,2482 +0,0 @@ -;;; -;;; ########################################################### -;;; ### NYQUIST-- A Language for Composition and Synthesis. ### -;;; ### ### -;;; ### Copyright (c) 1994-2006 by Roger B. Dannenberg ### -;;; ########################################################### -;;; -(princ "LOADING NYQUIST RUNTIME DEBUG VERSION\n") - -;; #### Error checking and reporting functions #### - -(setf *SAL-CALL-STACK* nil) ; because SEQ looks at this - -;; MULTICHANNEL-SOUNDP - test for vector of sounds -(defun multichannel-soundp (v) - (prog ((rslt t)) - (if (not (arrayp v)) (return nil)) - (dotimes (i (length v)) - (cond ((not (soundp (aref v i))) - (setf rslt nil) - (return nil)))) - (return rslt))) - -;; MULTICHANNELP - test for vector of sounds or numbers -(defun multichannelp (v) - (prog ((rslt t)) - (if (not (arrayp v)) (return nil)) - (dotimes (i (length v)) - (cond ((not (or (numberp (aref v i)) (soundp (aref v i)))) - (setf rslt nil) - (return nil)))) - (return rslt))) - -;; NUMBERSP - test for vector of numbers -(defun numbersp (v) - (prog ((rslt t)) - (if (not (arrayp v)) (return nil)) - (dotimes (i (length v)) - (cond ((not (numberp (aref v i))) - (setf rslt nil) - (return nil)))) - (return rslt))) - - -;; PARAM-TO-STRING - make printable parameter for error message -(defun param-to-string (param) - (cond ((null param) (format nil "NIL")) - ((soundp param) (format nil "a SOUND")) - ((multichannel-soundp param) - (format nil "a ~A-channel SOUND" (length param))) - ((eq (type-of param) 'ARRAY) ;; avoid saying "#(1 2), a ARRAY" - (format nil "~A, an ARRAY" param)) - ((stringp param) (format nil "~s, a STRING" param)) ;; add quotes - (t - (format nil "~A, a ~A" param (symbol-name (type-of param)))))) - - -;; NY:TYPECHECK -- syntactic sugar for "if", used for all nyquist typechecks -(setfn ny:typecheck if) - -(defun index-to-string (index) - (nth index '("" " 1st" " 2nd" " 3rd" " 4th" " 5th" " 6th" " 7th"))) - -(setf number-anon '((NUMBER) nil)) -(setf number-sound-anon '((NUMBER SOUND) nil)) - -;; NY:TYPE-LIST-AS-STRING - convert permissible type list into -;; description. E.g. typs = '(NUMBER SOUND) and multi = t returns: -;; "number, sound or array thereof" -(defun ny:type-list-as-string (typs multi) - (let (lis last penultimate (string "") multi-clause) - (if (member 'NUMBER typs) (push "number" lis)) - (if (member 'POSITIVE typs) (push "positive number" lis)) - (if (member 'NONNEGATIVE typs) (push "non-negative number" lis)) - (if (member 'INTEGER typs) (push "integer" lis)) - (if (member 'STEP typs) (push "step number" lis)) - (if (member 'STRING typs) (push "string" lis)) - (if (member 'SOUND typs) (push "sound" lis)) - (if (member 'NULL typs) (push "NIL" lis)) - ;; this should be handled with two entries: INTEGER and NULL, but - ;; this complicates multichan-expand, where lists of arbitrary types - ;; are not handled and we need INT-OR-NULL for PV-TIME-PITCH's - ;; hopsize parameter. - (cond ((member 'INT-OR-NULL typs) - (push "integer" lis) - (push "NIL" lis))) - (cond ((member 'POSITIVE-OR-NULL typs) - (push "positive number" lis) - (push "NIL" lis))) - (cond (multi - (setf multi-clause - (cond ((> (length lis) 1) "array thereof") - ((equal (car lis) "sound") "multichannel sound") - (t (strcat "array of " (car lis) "s")))) - (push multi-clause lis))) - (setf last (first lis)) - (setf penultimate (second lis)) - (setf lis (cddr lis)) - (dolist (item lis) - (setf string (strcat item ", " string))) - (strcat string (if penultimate (strcat penultimate " or ") "") last))) - - -;; NY:ERROR -- construct an error message and raise an error -(defun ny:error (src index typ val &optional multi (val2 nil second-val)) - (let ((types-string (ny:type-list-as-string (first typ) multi))) - (error (strcat "In " src "," (index-to-string index) " argument" - (if (second typ) (strcat " (" (second typ) ")") "") - (if (eq (char types-string 0) #\i) " must be an " " must be a ") - types-string - ", got " (param-to-string val) - (if second-val (strcat ", and" (param-to-string val2)) ""))))) - - -(prog () - (setq lppp -12.0) (setq lpp -9.0) (setq lp -6.0) (setq lmp -3.0) - (setq lfff 12.0) (setq lff 9.0) (setq lf 6.0) (setq lmf 3.0) - (setq dB0 1.00) (setq dB1 1.122) (setq dB10 3.1623) - - (setq s 0.25) (setq sd 0.375) (setq st (/ 0.5 3.0)) - (setq i 0.5) (setq id 0.75) (setq it (* st 2.0)) - (setq q 1.0) (setq qd 1.5) (setq qt (* st 4.0)) - (setq h 2.0) (setq hd 3.0) (setq ht (* st 8.0)) - (setq w 4.0) (setq wd 6.0) (setq wt (* st 16.0)) -) - -(init-global *A4-Hertz* 440.0) - -; next pitch, for initializations below -; -(defun np () (incf nyq:next-pitch)) - -(defun set-pitch-names () - (setq no-pitch 116.0) - ; note: 58.0 is A4 - (C0 - 1) = 69 - (12 - 1) - (setf nyq:next-pitch (- (hz-to-step *A4-Hertz*) 58.0)) - - (setf nyq:pitch-names - '(c0 (cs0 df0) d0 (ds0 ef0) e0 f0 (fs0 gf0) g0 (gs0 af0) a0 - (as0 bf0) b0 - c1 (cs1 df1) d1 (ds1 ef1) e1 f1 (fs1 gf1) g1 (gs1 af1) a1 - (as1 bf1) b1 - c2 (cs2 df2) d2 (ds2 ef2) e2 f2 (fs2 gf2) g2 (gs2 af2) a2 - (as2 bf2) b2 - c3 (cs3 df3) d3 (ds3 ef3) e3 f3 (fs3 gf3) g3 (gs3 af3) a3 - (as3 bf3) b3 - c4 (cs4 df4) d4 (ds4 ef4) e4 f4 (fs4 gf4) g4 (gs4 af4) a4 - (as4 bf4) b4 - c5 (cs5 df5) d5 (ds5 ef5) e5 f5 (fs5 gf5) g5 (gs5 af5) a5 - (as5 bf5) b5 - c6 (cs6 df6) d6 (ds6 ef6) e6 f6 (fs6 gf6) g6 (gs6 af6) a6 - (as6 bf6) b6 - c7 (cs7 df7) d7 (ds7 ef7) e7 f7 (fs7 gf7) g7 (gs7 af7) a7 - (as7 bf7) b7 - c8 (cs8 df8) d8 (ds8 ef8) e8 f8 (fs8 gf8) g8 (gs8 af8) a8 - (as8 bf8) b8)) - - (dolist (p nyq:pitch-names) - (cond ((atom p) (set p (np))) - (t (let ((pitch (np))) - (dolist (s p) (set s pitch))))))) - - -(set-pitch-names) - -(init-global *default-sound-srate* 44100.0) -(init-global *default-control-srate* 2205.0) - -(setf *environment-variables* - '(*WARP* *SUSTAIN* *START* *LOUD* *TRANSPOSE* - *STOP* *CONTROL-SRATE* *SOUND-SRATE*)) - -(setfn environment-time car) -(setfn environment-stretch cadr) - -; ENVIRONMENT-MAP - map virtual time using an environment -; -;(defun environment-map (env tim) -; (+ (environment-time env) -; (* (environment-stretch env) tim))) - - -(defun nyq:the-environment () (mapcar 'eval *environment-variables*)) - - -;; GLOBAL ENVIRONMENT VARIABLES and their startup values: -(defun nyq:environment-init () - (setq *WARP* '(0.0 1.0 nil)) - (setq *LOUD* 0.0) ; now in dB - (setq *TRANSPOSE* 0.0) - (setq *SUSTAIN* 1.0) - (setq *START* MIN-START-TIME) - (setq *STOP* MAX-STOP-TIME) - (setq *CONTROL-SRATE* *DEFAULT-CONTROL-SRATE*) - (setq *SOUND-SRATE* *DEFAULT-SOUND-SRATE*) - t) ; return nothing in particular - -(nyq:environment-init) - -(defun get-duration (dur) - (ny:typecheck (not (numberp dur)) - (ny:error "GET-DURATION" 0 number-anon dur)) - (let ((duration - (- (local-to-global (* (get-sustain) dur)) - (setf *rslt* (local-to-global 0))))) - (cond ((minusp duration) - (error -"duration is less than zero: perhaps a warp or stretch -is ill-formed. Nyquist cannot continue because synthesis -functions assume durations are always positive."))) - duration)) - - -(defun get-loud () - (cond ((numberp *loud*) *loud*) - ((soundp *loud*) - (sref *loud* 0)) - (t - (error (format t "*LOUD* should be a number or sound: ~A" *LOUD*))))) - - -(defun get-sustain () - (cond ((numberp *SUSTAIN*) *SUSTAIN*) - ((soundp *SUSTAIN*) - ;(display "get-sustain: lookup " (local-to-global 0) 0)) - (sref *SUSTAIN* 0)) - (t - (error (format t "*SUSTAIN* should be a number or sound: ~A" *SUSTAIN*))))) - - -(defun get-tempo () - (if (warp-function *WARP*) - (slope (snd-inverse (get-warp) (local-to-global 0) - *control-srate*)) - (/ 1.0 (warp-stretch *WARP*)))) - -(defun get-transpose () - (cond ((numberp *TRANSPOSE*) *TRANSPOSE*) - ((soundp *TRANSPOSE*) - (sref *TRANSPOSE* 0)) - (t - (error (format t "*TRANSPOSE* should be a number or sound: ~A" *TRANSPOSE*))))) - - -(defun get-warp () - (let ((f (warp-function *WARP*))) - (ny:typecheck (null f) - (error "In GET-WARP, there is no warp function, probably because you are not within WARP or WARP-ABS")) - (shift-time (scale-srate f (/ (warp-stretch *WARP*))) - (- (warp-time *WARP*))))) - - -(load "dspprims.lsp" :verbose NIL) -(load "fileio.lsp" :verbose NIL) - - -;;;;;;;;;;;;;;;;;;;;;; -;; OSCILATORS -;;;;;;;;;;;;;;;;;;;;;; - -(defun build-harmonic (n table-size) - (ny:typecheck (not (integerp n)) - (ny:error "BUILD-HARMONIC" 1 '((INTEGER) "n") n)) - (ny:typecheck (not (integerp table-size)) - (ny:error "BUILD-HARMONIC" 2 '((INTEGER) "table-size") table-size)) - (ny:typecheck (>= n (/ table-size 2)) - (error "In BUILD-HARMONIC, harmonic number should be less than half the table size" - (list n table-size))) - (snd-sine 0 n table-size 1)) - - -(setf *SINE-TABLE* (list (build-harmonic 1 2048) - (hz-to-step 1.0) - T)) -(setf *TABLE* *SINE-TABLE*) - - -(defun calculate-hz (pitch what &optional (max-fraction 0.5) maxlength) - (let ((hz (step-to-hz (+ pitch (get-transpose)))) - (octaves 0) original) - (setf original hz) - (while (>= hz (* *SOUND-SRATE* max-fraction)) - (setf octaves (1+ octaves) - hz (* hz 0.5))) - (cond ((> octaves 0) - (format t - "Warning: ~A frequency reduced by ~A octaves from ~A to ~A hz to avoid aliasing.\n" - what octaves original hz) - (setf octaves 0))) - (while (and maxlength (<= hz (/ *SOUND-SRATE* maxlength))) - (setf octaves (1+ octaves) - hz (* hz 2.0))) - (cond ((> octaves 0) - (format t - "Warning: ~A frequency increased by ~A octaves from ~A to ~A hz due to restriction on maximum table length.\n" - what octaves original hz))) - hz)) - - -(defun ny:assert-env-spec (env-spec message) - (if (not (ny:env-spec-p env-spec)) - (error message env-spec))) - - -(defun ny:assert-table (fun-name index formal actual) - (if (not (and (listp actual) (= 3 (length actual)))) - (error (format nil - "In ~A,~A argument (~A) should be a list of 3 elements, got ~A" - fun-name (index-to-string index) formal actual))) - (if (not (soundp (car actual))) - (error (format nil - "In ~A,~A argument (~A) should be a list beginning with a sound, got ~A" - fun-name (index-to-string index) formal actual))) - (if (not (numberp (second actual))) - (error (format nil - "In ~A,~A argument (~A) should be a list whose 2nd element is a step number (pitch), got ~A" - fun-name (index-to-string index) formal actual))) - (if (not (third actual)) - (error (format nil - "In ~A,~A argument (~A) should be a list whose 3rd element is true, got ~A" - fun-name (index-to-string index) formal actual)))) - - -(defun ny:assert-sample (fun-name index formal actual) - (if (not (and (listp actual) (= 3 (length actual)))) - (error (format nil - "In ~A,~A argument (~A) should be a list of 3 elements, got ~A" - fun-name (index-to-string index) formal actual))) - (if (not (soundp (car actual))) - (error (format nil - "In ~A,~A argument (~A) should be a list beginning with a sound, got ~A" - fun-name (index-to-string index) formal actual))) - (if (not (numberp (second actual))) - (error (format nil - "In ~A,~A argument (~A) should be a list whose 2nd element is a step number (pitch), got ~A" - fun-name (index-to-string index) formal actual))) - (if (not (numberp (third actual))) - (error (format nil - "In ~A,~A argument (~A) should be a list whose 3rd element is the sample start time, got ~A" - fun-name (index-to-string index) formal actual)))) - -(defun ny:env-spec-p (env-spec) - (prog (len (rslt t)) - (if (not (listp env-spec)) (return nil)) - (setf len (length env-spec)) - (if (< len 6) (return nil)) - (if (> len 7) (return nil)) - (dolist (x env-spec) - (cond ((not (numberp x)) - (setf rslt nil) - (return nil)))) - (return rslt))) - - -;; AMOSC -;; -(defun amosc (pitch modulation &optional (sound *table*) (phase 0.0)) - (ny:typecheck (not (numberp pitch)) - (ny:error "AMOSC" 1 '((STEP) "pitch") pitch)) - (ny:typecheck (not (soundp modulation)) - (ny:error "AMOSC" 2 '((SOUND) "modulation") modulation)) - (ny:assert-table "AMOSC" 3 "table" sound) - (ny:typecheck (not (numberp phase)) - (ny:error "AMOSC" 4 '((NUMBER) "phase") phase)) - (let ((modulation-srate (snd-srate modulation)) - (hz (calculate-hz pitch "amosc"))) - (ny:scale-db (get-loud) - (snd-amosc - (car sound) ; samples for table - (cadr sound) ; step represented by table - *SOUND-SRATE* ; output sample rate - hz ; output hz - (local-to-global 0) ; starting time - modulation ; modulation - phase)))) ; phase - - -;; FMOSC -;; -;; modulation rate must be less than or equal to sound-srate, so -;; force resampling and issue a warning if necessary. snd-fmosc can -;; handle upsampling cases internally. -;; -(defun fmosc (pitch modulation &optional (sound *table*) (phase 0.0)) - (ny:typecheck (not (numberp pitch)) - (ny:error "FMOSC" 1 '((STEP) "pitch") pitch)) - (ny:typecheck (not (soundp modulation)) - (ny:error "FMOSC" 2 '((SOUND) "modulation") modulation)) - (ny:assert-table "FMOSC" 3 "table" sound) - (ny:typecheck (not (numberp phase)) - (ny:error "FMOSC" 4 '((NUMBER) "phase") phase)) - (let ((modulation-srate (snd-srate modulation)) - (hz (calculate-hz pitch "fmosc"))) - (ny:scale-db (get-loud) - (snd-fmosc - (car sound) ; samples for table - (cadr sound) ; step represented by table - *SOUND-SRATE* ; output sample rate - hz ; output hz - (local-to-global 0) ; starting time - modulation ; modulation - phase)))) ; phase - - -;; FMFB -;; -;; this code is based on FMOSC above -;; -(defun fmfb (pitch index &optional (dur 1.0)) - (ny:typecheck (not (numberp pitch)) - (ny:error "FMFB" 1 '((STEP) "pitch") pitch)) - (ny:typecheck (not (or (numberp index) (soundp index))) - (ny:error "FMFB" 2 '((NUMBER SOUND) "index") index)) - (ny:typecheck (not (numberp dur)) - (ny:error "FMFB" 3 '((NUMBER) "dur") dur)) - (let ((hz (calculate-hz pitch "fmfb"))) - (setf dur (get-duration dur)) - (cond ((soundp index) (ny:fmfbv hz index)) - (t - (ny:scale-db (get-loud) - (snd-fmfb (local-to-global 0) - hz *SOUND-SRATE* index dur)))))) - -;; private variable index version of fmfb -(defun ny:fmfbv (hz index) - (let ((modulation-srate (snd-srate index))) - (cond ((< *SOUND-SRATE* modulation-srate) - (format t "Warning: down-sampling FM modulation in fmfb~%") - (setf index (snd-down *SOUND-SRATE* index)))) - (ny:scale-db (get-loud) - (snd-fmfbv (local-to-global 0) hz *SOUND-SRATE* index)))) - - -;; BUZZ -;; -;; (ARGUMENTS ("long" "n") ("rate_type" "sr") ("double" "hz") -;; ("time_type" "t0") ("sound_type" "s_fm")) -;; -(defun buzz (n pitch modulation) - (ny:typecheck (not (integerp n)) - (ny:error "BUZZ" 1 '((INTEGER) "number of harmonics") n)) - (ny:typecheck (not (numberp pitch)) - (ny:error "BUZZ" 2 '((STEP) "pitch") pitch)) - (ny:typecheck (not (soundp modulation)) - (ny:error "BUZZ" 3 '((SOUND) "modulation") modulation)) - (let ((modulation-srate (snd-srate modulation)) - (hz (calculate-hz pitch "buzz nominal"))) - (cond ((< *SOUND-SRATE* modulation-srate) - (format t "Warning: down-sampling modulation in buzz~%") - (setf modulation (snd-down *SOUND-SRATE* modulation)))) - (setf n (max n 1)) ; avoid divide by zero problem - (ny:scale-db (get-loud) - (snd-buzz n ; number of harmonics - *SOUND-SRATE* ; output sample rate - hz ; output hz - (local-to-global 0) ; starting time - modulation)))) ; freq. modulation - - -;; (HZOSC hz [table [phase]]) -;; -;; similar to FMOSC, but without "carrier" frequency parameter -;; also, hz may be a scalar or a sound -;; -(defun hzosc (hz &optional (sound *table*) (phase 0.0)) - (ny:typecheck (not (or (numberp hz) (soundp hz))) - (ny:error "HZOSC" 1 '((NUMBER SOUND) "hz") hz)) - (ny:assert-table "HZOSC" 2 "table" sound) - (ny:typecheck (not (numberp phase)) - (ny:error "HZOSC" 3 '((NUMBER) "phase") phase)) - (let (hz-srate) - (cond ((numberp hz) - (osc (hz-to-step hz) 1.0 sound phase)) - (t - (setf hz-srate (snd-srate hz)) - (cond ((< *SOUND-SRATE* hz-srate) - (format t "Warning: down-sampling hz in hzosc~%") - (setf hz (snd-down *SOUND-SRATE* hz)))) - (ny:scale-db (get-loud) - (snd-fmosc (car sound) ; samples for table - (cadr sound) ; step repr. by table - *SOUND-SRATE* ; output sample rate - 0.0 ; dummy carrier - (local-to-global 0) ; starting time - hz phase)))))) - - -;; (SIOSC-BREAKPOINTS tab0 t1 tab1 ... tn tabn) -;; converts times to sample numbers -;; NOTE: time-warping the spectral envelope seems -;; like the wrong thing to do (wouldn't it be better -;; to warp the parameters that control the spectra, -;; or don't warp at all?). Nominally, a note should -;; have a "score" or local time duration equal to the -;; SUSTAIN environment variable. (When sustain is 1.0 -;; and no time-warping is in effect, the duration is 1). -;; So, scale all times by -;; (local-to-global (get-sustain)) -;; so that if the final time tn = 1.0, we get a nominal -;; length note. - -(defun siosc-breakpoints (breakpoints) - (prog (sample-count result (last-count 0) time-factor (index 0)) - (setf time-factor - (- (local-to-global (get-sustain)) - (local-to-global 0.0))) - (setf time-factor (* time-factor *SOUND-SRATE*)) - (ny:typecheck (not (and (listp breakpoints) - (cdr breakpoints) - (cddr breakpoints))) - (error "In SIOSC, 3rd argument (breakpoints) must be a list with at least 3 elements" - breakpoints)) -loop - (ny:typecheck (not (and (listp breakpoints) - (soundp (car breakpoints)))) - (error (format nil - "In SIOSC, expected a sound in breakpoints list at index ~A" - index) - (car breakpoints))) - (push (car breakpoints) result) - (setf breakpoints (cdr breakpoints)) - (setf index (1+ index)) - (cond (breakpoints - (ny:typecheck (not (and (listp breakpoints) - (numberp (car breakpoints)))) - (error (format nil - "In SIOSC, expected a number (time) in breakpoints list at index ~A" - index) - (car breakpoints))) - (setf sample-count (truncate - (+ 0.5 (* time-factor (car breakpoints))))) - (cond ((< sample-count last-count) - (setf sample-count (1+ last-count)))) - (push sample-count result) - (setf last-count sample-count) - (setf breakpoints (cdr breakpoints)) - (setf index (1+ index)) - (cond (breakpoints - (go loop))))) - (setf result (reverse result)) - (return result))) - - -;; SIOSC -- spectral interpolation oscillator -;; -;; modulation rate must be less than or equal to sound-srate, so -;; force resampling and issue a warning if necessary. snd-fmosc can -;; handle upsampling cases internally. -;; -(defun siosc (pitch modulation breakpoints) - (ny:typecheck (not (numberp pitch)) - (ny:error "SIOSC" 1 '((STEP) "pitch") pitch)) - (ny:typecheck (not (soundp modulation)) - (ny:error "SIOSC" 2 '((SOUND) "modulation") modulation)) - (let ((modulation-srate (snd-srate modulation)) - (hz (calculate-hz pitch "siosc nominal"))) - (cond ((< *SOUND-SRATE* modulation-srate) - (format t "Warning: down-sampling FM modulation in siosc~%") - (setf modulation (snd-down *SOUND-SRATE* modulation)))) - (ny:scale-db (get-loud) - (snd-siosc (siosc-breakpoints breakpoints) ; tables - *SOUND-SRATE* ; output sample rate - hz ; output hz - (local-to-global 0) ; starting time - modulation)))) ; modulation - - -;; LFO -- freq &optional duration sound phase) -;; -;; Default duration is 1.0 sec, default sound is *TABLE*, -;; default phase is 0.0. -;; -(defun lfo (freq &optional (duration 1.0) - (sound *SINE-TABLE*) (phase 0.0)) - (ny:typecheck (not (numberp freq)) - (ny:error "LFO" 1 '((NUMBER) "freq") freq)) - (ny:typecheck (not (numberp duration)) - (ny:error "LFO" 2 '((NUMBER) "duration") duration)) - (ny:assert-table "LFO" 3 "table" sound) - (ny:typecheck (not (numberp phase)) - (ny:error "LFO" 4 '((NUMBER) "phase") phase)) - (let ((d (get-duration duration))) - (if (minusp d) (setf d 0)) - (cond ((> freq (/ *CONTROL-SRATE* 2)) - (format t "Warning: lfo frequency (~A hz) will alias at current control rate (~A hz).\n" - freq *CONTROL-SRATE*))) - (ny:set-logical-stop - (snd-osc - (car sound) ; samples for table - (cadr sound) ; step represented by table - *CONTROL-SRATE* ; output sample rate - freq ; output hz - *rslt* ; starting time - d ; duration - phase) ; phase - duration))) - - -;; FMLFO -- like LFO but uses frequency modulation -;; -(defun fmlfo (freq &optional (sound *SINE-TABLE*) (phase 0.0)) - (ny:typecheck (not (soundp freq)) - (ny:error "FMLFO" 1 '((SOUND) "freq") freq)) - (ny:assert-table "FMLFO" 2 "table" sound) - (ny:typecheck (not (numberp phase)) - (ny:error "FMLFO" 3 '((NUMBER) "phase") phase)) - (let () - (cond ((numberp freq) - (lfo freq 1.0 sound phase)) - ((soundp freq) - (cond ((> (snd-srate freq) *CONTROL-SRATE*) - (setf freq (force-srate *CONTROL-SRATE* freq)))) - (snd-fmosc (car sound) (cadr sound) *CONTROL-SRATE* 0.0 - (local-to-global 0) freq phase)) - (t - (error "frequency must be a number or sound"))))) - - -;; OSC - table lookup oscillator -;; -(defun osc (pitch &optional (duration 1.0) - (sound *TABLE*) (phase 0.0)) - (ny:typecheck (not (numberp pitch)) - (ny:error "OSC" 1 '((STEP) "pitch") pitch)) - (ny:typecheck (not (numberp duration)) - (ny:error "OSC" 2 '((NUMBER) "duration") duration)) - (ny:assert-table "OSC" 3 "table" sound) - (ny:typecheck (not (numberp phase)) - (ny:error "OSC" 4 '((NUMBER) "phase") phase)) - (let ((d (get-duration duration)) - (hz (calculate-hz pitch "osc"))) - (ny:set-logical-stop - (snd-scale (db-to-linear (get-loud)) - (snd-osc - (car sound) ; samples for table - (cadr sound) ; step represented by table - *SOUND-SRATE* ; output sample rate - hz ; output hz - *rslt* ; starting time - d ; duration - phase)) ; phase - duration))) - - -;; PARTIAL -- sine osc with built-in envelope scaling -;; -(defun partial (steps env) - (ny:typecheck (not (numberp steps)) - (ny:error "PARTIAL" 1 '((STEP) "steps") steps)) - (ny:typecheck (not (soundp env)) - (ny:error "PARTIAL" 2 '((SOUND) "env") env)) - (let ((hz (calculate-hz steps "partial"))) - (ny:scale-db (get-loud) - (snd-partial *sound-srate* hz - (force-srate *sound-srate* env))))) - - -(setf *SINE-SAMPLE* (list (first *TABLE*) (second *TABLE*) 0.0)) - - -;; SAMPLER -- simple attack + sustain sampler -;; -(defun sampler (pitch modulation - &optional (sample *SINE-SAMPLE*) (npoints 2)) - (ny:typecheck (not (numberp pitch)) - (ny:error "SAMPLER" 1 '((STEP) "pitch") pitch)) - (ny:typecheck (not (soundp modulation)) - (ny:error "SAMPLER" 2 '((SOUND) "modulation") modulation)) - (ny:assert-sample "SAMPLER" 3 "table" sample) - (ny:typecheck (not (integerp npoints)) - (ny:error "SAMPLER" 3 '((INTEGER) "npoints") npoints)) - (let ((samp (car sample)) - (samp-pitch (cadr sample)) - (samp-loop-start (caddr sample)) - (hz (calculate-hz pitch "sampler nominal"))) - ; make a waveform table look like a sample with no attack: - (cond ((not (numberp samp-loop-start)) - (setf samp-loop-start 0.0))) - (ny:scale-db (get-loud) - (snd-sampler - samp ; samples for table - samp-pitch ; step represented by table - samp-loop-start ; time to start loop - *SOUND-SRATE* ; output sample rate - hz ; output hz - (local-to-global 0) ; starting time - modulation ; modulation - npoints)))) ; number of interpolation points - - -;; SINE -- simple sine oscillator -;; -(defun sine (steps &optional (duration 1.0)) - (ny:typecheck (not (numberp steps)) - (ny:error "SINE" 1 '((STEP) "steps") steps)) - (ny:typecheck (not (numberp duration)) - (ny:error "SINE" 2 '((NUMBER) "duration") duration)) - (let ((hz (calculate-hz steps "sine")) - (d (get-duration duration))) - (ny:set-logical-stop - (ny:scale-db (get-loud) - (snd-sine *rslt* hz *sound-srate* d)) - duration))) - - -;; PLUCK -;; -;; (ARGUMENTS ("double" "sr") ("double" "hz") ("time_type" "t0") -;; ("time_type" "d") ("double" "final_amp")) -;; -(defun pluck (steps &optional (duration 1.0) (final-amp 0.001)) - (ny:typecheck (not (numberp steps)) - (ny:error "PLUCK" 1 '((NUMBER) "steps") steps)) - (ny:typecheck (not (numberp duration)) - (ny:error "PLUCK" 2 '((NUMBER) "duration") duration)) - (ny:typecheck (not (numberp final-amp)) - (ny:error "PLUCK" 3 '((NUMBER) "final-amp") final-amp)) - ;; 200000 is MAXLENGTH in nyquist/tran/pluck.alg - the max table length - (let ((hz (calculate-hz steps "pluck" (/ 1.0 3) 200000)) - (d (get-duration duration))) - (ny:set-logical-stop - (ny:scale-db (get-loud) - (snd-pluck *SOUND-SRATE* hz *rslt* d final-amp)) - duration))) - - -;; abs-env -- restore the standard environment -;; -(defmacro abs-env (s) - `(progv '(*WARP* *LOUD* *TRANSPOSE* *SUSTAIN* - *START* *STOP* - *CONTROL-SRATE* *SOUND-SRATE*) - (list '(0.0 1.0 NIL) 0.0 0.0 1.0 - MIN-START-TIME MAX-STOP-TIME - *DEFAULT-CONTROL-SRATE* *DEFAULT-SOUND-SRATE*) - ,s)) - - -;; (NYQ:TO-ARRAY SOUND N) - duplicate SOUND to N channels -; -(defun nyq:to-array (value len) - (let ((a (make-array len))) - (dotimes (i len) - (setf (aref a i) value)) - a)) - - -; nyq:add2 - add two arguments. -; -; Assumes s1 and s2 are numbers, sounds, or multichannel sounds or numbers -; -; Semantics: numbers and sounds can be freely mixed and -; add as expected. Arrays (multichannel) arguments are -; added channel-by-channel, and if one array is larger, -; the "extra" channels are simply copied to the result. -; Therefore the result has the channel count of the -; maximum channel count in s1 or s2. When adding a -; multichannel sound to a (non-multichannel) sound, the -; sound is coerced to a 1-channel multi-channel sound, -; and therefore adds to channel 1 of the multi-channel -; sound. However, when adding a multichannel sound to a -; number, the number is added to *every* channel. -; Semantics differ from the normal multichan-expand processing -; in that sounds are considered to be a multichannel sound -; with 1 channel, and channel counts do not have to match -; when processing array arguments. -; -(defun nyq:add2 (s1 s2) - ; make number + number as fast as possible: - (cond ((and (numberp s1) (numberp s2)) (+ s1 s2)) - ; if not 2 numbers, the overhead here is amortized by - ; computing samples of at least one sound - ((and (arrayp s1) (numberp s2)) - (sum-of-arrays s1 (nyq:to-array s2 (length s1)))) - ((and (arrayp s2) (numberp s1)) - (sum-of-arrays (nyq:to-array s1 (length s2)) s2)) - ((and (arrayp s1) (soundp s2)) - (sum-of-arrays s1 (vector s2))) - ((and (arrayp s2) (soundp s1)) - (sum-of-arrays (vector s1) s2)) - ((and (arrayp s1) (arrayp s2)) - (sum-of-arrays s1 s2)) - ((numberp s1) - (snd-offset s2 s1)) - ((numberp s2) - (snd-offset s1 s2)) - (t - (nyq:add-2-sounds s1 s2)))) - - -; (NYQ:ADD-2-SOUNDS S1 S2) - add two sound arguments -; -; assumes s1 and s2 are sounds -; -(defun nyq:add-2-sounds (s1 s2) - (let ((s1sr (snd-srate s1)) - (s2sr (snd-srate s2))) - (cond ((> s1sr s2sr) - (snd-add s1 (snd-up s1sr s2))) - ((< s1sr s2sr) - (snd-add (snd-up s2sr s1) s2)) - (t - (snd-add s1 s2))))) - - -(defmacro at (x s) - `(progv '(*WARP*) - (let ((shift ,x)) - (ny:typecheck (not (numberp shift)) - (error "1st argument of AT (or 2nd argument of SAL's @ operator) should be a time offset number" shift)) - (list (list (+ (warp-time *WARP*) - (* (warp-stretch *WARP*) shift)) - (warp-stretch *WARP*) - (warp-function *WARP*)))) - ,s)) - - -;; (AT-ABS t behavior) evaluate behavior at global time t -;; -;; *WARP* is the triple (d s f) denoting the function f(st+d), -;; a mapping from local to global time. -;; We want (d' s f) such that f(s*0 + d') = t -;; (Note that we keep the same s and f, and only change the offset. -;; To eliminate the warp and stretch use "(abs-env (at t behavior))") -;; Applying the inverse of f, d' = f-1(t), or (sref (snd-inverse f ...) t) -;; Rather than invert the entire function just to evaluate at one point, -;; we use SREF-INVERSE to find d'. -;; -(defmacro at-abs (x s) - `(progv '(*WARP*) - (let ((tim ,x)) - (ny:typecheck (not (numberp tim)) - (error "1st argument of AT-ABS (or 2nd argument of SAL's @@ operator) should be a number (start time)" tim)) - (if (warp-function *WARP*) - (list (list (sref-inverse (warp-function *WARP*) tim) - (warp-stretch *WARP*) - (warp-function *WARP*))) - (list (list tim (warp-stretch *WARP*) NIL)))) - ;; issue warning if sound starts in the past - (check-t0 ,s ',s))) - - -(defun check-t0 (s src) - (let (flag t0 (now (local-to-global 0))) - (cond ((arrayp s) - (dotimes (i (length s)) - (setf t0 (snd-t0 (aref s i)))) - (if (< t0 now) (setf flag t0))) - (t - (setf t0 (snd-t0 s)) - (if (< t0 now) (setf flag t0)))) - (if flag - (format t "Warning: cannot go back in time to ~A, sound came from ~A~%" - flag src)) - ; (display "check-t0" t0 now src) - ; return s whether or not warning was reported - s)) - -;; (CLIP S1 VALUE) - clip maximum amplitude to value -; -(defun clip (x v) - (ny:typecheck (not (or (numberp x) (soundp x) (multichannelp x))) - (ny:error "CLIP" 1 number-sound-anon x t)) - (ny:typecheck (not (numberp v)) - (ny:error "CLIP" 2 number-anon v)) - (cond ((numberp x) - (max (min x v) (- v))) - ((arrayp x) - (let* ((len (length x)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) - (snd-clip (aref x i) v))) - result)) - (t ;; x is a sound - (snd-clip x v)))) - - -;; (NYQ:COERCE-TO S1 S2) - expand sound s1 to type of s2 -; -(defun nyq:coerce-to (s1 s2) - (cond ((or (soundp s1) (numberp s1)) - (cond ((arrayp s2) - (nyq:to-array s1 (length s2))) - (t s1))) - (t s1))) - - -(defmacro continuous-control-warp (beh) - `(snd-compose (warp-abs nil ,beh) - (snd-inverse (get-warp) - (local-to-global 0) *control-srate*))) - -(defmacro continuous-sound-warp (beh) - `(snd-compose (warp-abs nil ,beh) - (snd-inverse (get-warp) - (local-to-global 0) *sound-srate*))) - - -(defmacro control-srate-abs (r s) - `(let ((rate ,r)) - (progv '(*CONTROL-SRATE*) - (progn (ny:typecheck (not (numberp rate)) - (ny:error "CONTROL-SRATE-ABS" 1 '((NUMBER) "sample rate") rate)) - (list rate)) - ,s))) - -; db = 20log(ratio) -; db = 20 ln(ratio)/ln(10) -; db/20 = ln(ratio)/ln(10) -; db ln(10)/20 = ln(ratio) -; e^(db ln(10)/20) = ratio -; -(setf ln10over20 (/ (log 10.0) 20)) - -(defun db-to-linear (x) - (ny:typecheck (not (or (numberp x) (soundp x) (multichannelp x))) - (ny:error "DB-TO-LINEAR" 0 number-sound-anon x t)) - (cond ((numberp x) - (exp (* ln10over20 x))) - ((arrayp x) - (let* ((len (length x)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) - (snd-exp (snd-scale ln10over20 (aref x i))))) - result)) - (t - (snd-exp (snd-scale ln10over20 x))))) - - -(defun linear-to-db (x) - (ny:typecheck (not (or (numberp x) (soundp x) (multichannelp x))) - (ny:error "LINEAR-TO-DB" 0 number-sound-anon x t)) - (cond ((numberp x) - (/ (log (float x)) ln10over20)) - ((arrayp x) - (let* ((len (length x)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) - (snd-scale (/ 1.0 ln10over20) (snd-log (aref x i))))) - result)) - (t - (snd-scale (/ 1.0 ln10over20) (snd-log x))))) - - -(cond ((not (fboundp 'scalar-step-to-hz)) - (setfn scalar-step-to-hz step-to-hz) - (setfn scalar-hz-to-step hz-to-step))) - - -(defun step-to-hz (x) - (ny:typecheck (not (or (numberp x) (soundp x) (multichannelp x))) - (ny:error "STEP-TO-HZ" 0 number-sound-anon x t)) - (cond ((numberp x) - (scalar-step-to-hz x)) - ((arrayp x) - (let* ((len (length x)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) (step-to-hz (aref x i)))) - result)) - (t - (s-exp (snd-offset (snd-scale 0.0577622650466621 x) - 2.1011784386926213))))) - -(defun hz-to-step (x) - (ny:typecheck (not (or (numberp x) (soundp x) (multichannelp x))) - (ny:error "HZ-TO-STEP" 0 number-sound-anon x t)) - (cond ((numberp x) - (scalar-hz-to-step x)) - ((arrayp x) - (let* ((len (length x)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) (hz-to-step (aref x i)))) - result)) - (t - (snd-scale 17.312340490667565 - (snd-offset (s-log x) -2.1011784386926213))))) - - -; sref - access a sound at a given time point -; note that the time is transformed to global -(defun sref (sound point) - (ny:typecheck (not (soundp sound)) - (ny:error "SREF" 1 '((SOUND) "sound") sound)) - (ny:typecheck (not (numberp point)) - (ny:error "SREF" 2 '((NUMBER) "time") point)) - (snd-sref sound (local-to-global point))) - - -; extract - start is stretched and shifted as is stop -; result is shifted to start at local time zero -(defun extract (start stop sound) - (ny:typecheck (not (numberp start)) - (ny:error "EXTRACT" 1 '((NUMBER) "start") start)) - (ny:typecheck (not (numberp stop)) - (ny:error "EXTRACT" 2 '((NUMBER) "stop") stop)) - (ny:typecheck (< stop start) - (error - (format nil "In EXTRACT, stop (~A) must be greater or equal to start (~A)" - stop start))) - (ny:typecheck (not (soundp sound)) - (ny:error "EXTRACT" 3 '((SOUND) "sound") sound)) - (extract-abs (local-to-global start) (local-to-global stop) sound - (local-to-global 0))) - -; extract-abs - return sound between start and stop -; start-time is optional (to aid the implementation of -; extract) and gives the start time of the result, normally 0. -; There is a problem if sound t0 is not equal to start-time. -; E.g. if sound was created with AT, its t0 might be -; in the future, but snd-xform works by first shifting -; t0 to local time zero, so we need to be very careful. -; The solution is that if t0 > start_time, subtract the difference -; from start and stop to shift them appropriately. -(defun extract-abs (start stop sound &optional (start-time 0)) - (ny:typecheck (not (numberp start)) - (ny:error "EXTRACT-ABS" 1 '((NUMBER) "start") start)) - (ny:typecheck (not (numberp stop)) - (ny:error "EXTRACT-ABS" 2 '((NUMBER) "stop") stop)) - (ny:typecheck (< stop start) - (error - (format nil - "In EXTRACT-ABS, stop (~A) must be greater or equal to start (~A)" - stop start))) - (ny:typecheck (not (soundp sound)) - (ny:error "EXTRACT-ABS" 3 '((SOUND) "sound") sound)) - (ny:typecheck (not (numberp start-time)) - (ny:error "EXTRACT-ABS" 4 '((NUMBER) "start-time") start-time)) - (let ((t0 (snd-t0 sound)) offset) - (cond ((/= t0 start-time) - (setf offset (- t0 start-time)) - (setf start (- start offset)) - (setf stop (- stop offset)))) - (snd-xform sound (snd-srate sound) start-time start stop 1.0))) - - -(defun local-to-global (local-time) - (ny:typecheck (not (numberp local-time)) - (ny:error "LOCAL-TO-GLOBAL" 0 '((NUMBER) "local-time") local-time)) - (let ((d (warp-time *WARP*)) - (s (warp-stretch *WARP*)) - (w (warp-function *WARP*)) - global-time) - (setf global-time (+ (* s local-time) d)) - (if w (snd-sref w global-time) global-time))) - - -(defmacro loud (x s) - `(progv '(*LOUD*) - (let ((ld ,x)) - (ny:typecheck (not (or (numberp ld) (soundp ld))) - (ny:error "LOUD" 1 number-sound-anon ld)) - (list (sum *LOUD* ld))) - ,s)) - - -(defmacro loud-abs (x s) - `(progv '(*LOUD*) - (let ((ld ,x)) - (ny:typecheck (not (or (numberp ld) (soundp ld))) - (ny:error "LOUD-ABS" 1 number-anon ld)) - (list ld)) - ,s)) - - -;(defun must-be-sound (x) -; (cond ((soundp x) x) -; (t -; (error "SOUND type expected" x)))) - - -;; NY:SCALE-DB -- a "fast" scale-db: no typechecks and -;; no multichannel expansion -(defun ny:scale-db (factor sound) - (snd-scale (db-to-linear factor) sound)) - - -;; SCALE-DB -- same as scale, but argument is in db -;; -(defun scale-db (factor sound) -; (ny:typecheck (not (or (numberp factor) (numbersp factor))) -; (ny:error "SCALE-DB" 1 '((NUMBER) "dB") factor t)) -; (ny:typecheck (not (or (soundp sound) (multichannel-soundp sound))) -; (ny:error "SCALE-DB" 2 '((SOUND) "sound") sound t)) - (multichan-expand "SCALE-DB" #'ny:scale-db - '(((NUMBER) "factor") ((SOUND) "sound")) factor sound)) - - - -(defun set-control-srate (rate) - (ny:typecheck (not (numberp rate)) - (ny:error "SET-CONTROL-SRATE" 0 '((NUMBER) "rate") rate)) - (setf *default-control-srate* (float rate)) - (nyq:environment-init)) - -(defun set-sound-srate (rate) - (ny:typecheck (not (numberp rate)) - (ny:error "SET-SOUND-SRATE" 0 '((NUMBER) "rate") rate)) - (setf *default-sound-srate* (float rate)) - (nyq:environment-init)) - - -; s-plot -- compute and write n data points for plotting -; -; dur is how many seconds of sound to plot. If necessary, cut the -; sample rate to allow plotting dur seconds -; n is the number of points to plot. If there are more than n points, -; cut the sample rate. If there are fewer than n samples, just -; plot the points that exist. -; -(defun s-plot (snd &optional (dur 2.0) (n 1000)) - (ny:typecheck (not (soundp snd)) - (ny:error "S-PLOT (or PLOT command)" 1 '((SOUND) nil) snd)) - (ny:typecheck (not (numberp dur)) - (ny:error "S-PLOT (or PLOT command)" 2 '((NUMBER) "dur") dur)) - (ny:typecheck (not (integerp n)) - (ny:error "S-PLOT (or PLOT command)" 3 '((INTEGER) nil) n)) - - (prog* ((sr (snd-srate snd)) - (t0 (snd-t0 snd)) - (filename (soundfilename *default-plot-file*)) - (s snd) ;; s is either snd or resampled copy of snd - (outf (open filename :direction :output)) ;; for plot data - (maximum -1000000.0) ;; maximum amplitude - (minimum 1000000.0) ;; minimum amplitude - actual-dur ;; is the actual-duration of snd - sample-count ;; is how many samples to get from s - period ;; is the period of samples to be plotted - truncation-flag ;; true if we didn't get whole sound - points) ;; is array of samples - ;; If we need more than n samples to get dur seconds, resample - (cond ((< n (* dur sr)) - (setf s (force-srate (/ (float n) dur) snd)))) - ;; Get samples from the signal - (setf points (snd-samples s (1+ n))) - ;; If we got fewer than n points, we can at least estimate the - ;; actual duration (we might not know exactly if we use a lowered - ;; sample rate). If the actual sample rate was lowered to avoid - ;; getting more than n samples, we can now raise the sample rate - ;; based on our estimate of the actual sample duration. - ;(display "test" (length points) n) - (cond ((< (length points) n) - ;; sound is shorter than dur, estimate actual length - (setf actual-dur (/ (length points) (snd-srate s))) - (setf sample-count (round (min n (* actual-dur sr)))) - (cond ((< n (* actual-dur sr)) - (setf s (force-srate (/ (float n) actual-dur) snd))) - (t ;; we can use original signal - (setf s snd))) - (setf points (snd-samples s sample-count)) - ;; due to rounding, need to recalculate exact count - (setf sample-count (length points))) - ((= (length points) n) - (setf actual-dur dur) - (setf sample-count n)) - (t ;; greater than n points, so we must have truncated sound - (setf actual-dur dur) - (setf sample-count n) - (setf truncation-flag t))) - ;; actual-dur is the duration of the plot - ;; sample-count is how many samples we have - (setf period (/ 1.0 (snd-srate s))) - (cond ((null outf) - (format t "s-plot: could not open ~A!~%" filename) - (return nil))) - (format t "s-plot: writing ~A ... ~%" filename) - (cond (truncation-flag - (format t " !!TRUNCATING SOUND TO ~As\n" actual-dur))) - (cond ((/= (snd-srate s) (snd-srate snd)) - (format t " !!RESAMPLING SOUND FROM ~A to ~Ahz\n" - (snd-srate snd) (snd-srate s)))) - (cond (truncation-flag - (format t " Plotting ~As, actual sound duration is greater\n" - actual-dur)) - (t - (format t " Sound duration is ~As~%" actual-dur))) - (dotimes (i sample-count) - (setf maximum (max maximum (aref points i))) - (setf minimum (min minimum (aref points i))) - (format outf "~A ~A~%" (+ t0 (* i period)) (aref points i))) - (close outf) - (format t " Wrote ~A points from ~As to ~As~%" - sample-count t0 (+ t0 actual-dur)) - (format t " Range of values ~A to ~A\n" minimum maximum) - (cond ((or (< minimum -1) (> maximum 1)) - (format t " !!SIGNAL EXCEEDS +/-1~%"))))) - - -; run something like this to plot the points: -; graph < points.dat | plot -Ttek - -(defmacro sound-srate-abs (r s) - `(progv '(*SOUND-SRATE*) - (let ((rate ,r)) - (ny:typecheck (not (numberp rate)) - (ny:error "SOUND-SRATE-ABS" 1 '((NUMBER) "sample rate") rate)) - (list rate)) - ,s)) - - -(defmacro stretch (x s) - `(progv '(*WARP*) - (let ((str ,x)) - (ny:typecheck (not (numberp str)) - (error "1st argument of STRETCH (or 2nd argument of SAL's ~ operator) should be a number (stretch factor)" str)) - (list (list (warp-time *WARP*) - (* (warp-stretch *WARP*) str) - (warp-function *WARP*)))) - (ny:typecheck (minusp (warp-stretch *WARP*)) - (error "In STRETCH (or SAL's ~ operator), negative stretch factor is not allowed" - (warp-stretch *WARP*))) - ,s)) - - -(defmacro stretch-abs (x s) - `(progv '(*WARP*) - (let ((str ,x)) - (ny:typecheck (not (numberp str)) - (error "1st argument of STRETCH-ABS (or 2nd argument of SAL's ~~ operator) should be a number (stretch factor)" str)) - (list (list (local-to-global 0) str nil))) - (ny:typecheck (minusp (warp-stretch *WARP*)) - (error "In STRETCH-ABS (or SAL's ~~ operator), negative stretch factor is not allowed" - (warp-stretch *WARP*))) - ,s)) - - -(defmacro sustain (x s) - `(progv '(*SUSTAIN*) - (let ((sus ,x)) - (ny:typecheck (not (or (numberp sus) (soundp sus))) - (ny:error "SUSTAIN" 1 number-sound-anon sus)) - (list (prod *SUSTAIN* sus))) - ,s)) - - -(defmacro sustain-abs (x s) - `(progv '(*SUSTAIN*) - (let ((sus ,x)) - (ny:typecheck (not (or (numberp sus) (soundp sus))) - (ny:error "SUSTAIN-ABS" 1 number-sound-anon sus)) - (list sus)) - ,s)) - - -;; (WARP-FUNCTION *WARP*) - extracts function field of warp triple -;; -(setfn warp-function caddr) - - -;; (WARP-STRETCH *WARP*) - extracts stretch field of warp triple -;; -(setfn warp-stretch cadr) - - -;; (WARP-TIME *WARP*) - extracts time field of warp triple -;; -(setfn warp-time car) - - -(defmacro transpose (x s) - `(progv '(*TRANSPOSE*) - (let ((amt ,x)) - (ny:typecheck (not (or (numberp amt) (soundp amt))) - (ny:error "TRANSPOSE" 1 number-sound-anon amt)) - (list (sum *TRANSPOSE* amt))) - ,s)) - - -(defmacro transpose-abs (x s) - `(progv '(*TRANSPOSE*) - (let ((amt ,x)) - (ny:typecheck (not (or (numberp amt) (soundp amt))) - (ny:error "TRANSPOSE-ABS" 1 number-anon amt)) - (list amt)) - ,s)) - - -;; CONTROL-WARP -- apply a warp function to a control function -;; -(defun control-warp (warp-fn control &optional wrate) - (ny:typecheck (not (soundp warp-fn)) - (ny:error "CONTROL-WARP" 1 '((SOUND) "warp-fn") warp-fn)) - (ny:typecheck (not (soundp control)) - (ny:error "CONTROL-WARP" 2 '((SOUND) "control") control)) - (cond (wrate - (ny:typecheck (not (numberp wrate)) - (ny:error "CONTROL-WARP" 3 '((NUMBER) "wrate") wrate)) - (snd-resamplev control *control-srate* - (snd-inverse warp-fn (local-to-global 0) wrate))) - (t - (snd-compose control - (snd-inverse warp-fn (local-to-global 0) *control-srate*))))) - - -;; (cue sound) -;; Cues the given sound; that is, it applies the current *WARP*, *LOUD*, -;; *START*, and *STOP* values to the argument. The logical start time is at -;; local time 0. -(defun cue (sound) - (ny:typecheck (not (or (soundp sound) (multichannel-soundp sound))) - (ny:error "CUE" 0 '((SOUND) nil) sound t)) - (cond ((arrayp sound) - (let* ((len (length sound)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) - (cue-sound (aref sound i)))) - result)) - (t - (cue-sound sound)))) - -(defun cue-sound (sound) - (snd-xform sound - (snd-srate sound) - (local-to-global 0) *START* *STOP* (db-to-linear (get-loud)))) - -;; (sound sound) -;; Same as (cue sound), except also warps the sound. -;; Note that the *WARP* can change the pitch of the -;; sound as a result of resampling. -;; Here's the derivation for the warping code: -;; *WARP* is a triple: (d s f) which denotes that the warp from local to -;; global time is: f(st+d) -;; We need to compose sound with the inverse of this to get a function -;; of global time -;; Let f-1 be the inverse of f. Then the inverse of f(st+d) is -;; (f-1(t) - d)/s -;; The composition gives us: (snd-compose sound (f-1(t) - d)/s) -;; Eliminate the 1/s term by changing the sample rate of sound: -;; = (snd-compose (snd-scale-srate sound s) (f-1(t) - d)) -;; Eliminate the -d term by shifting f before taking the inverse: -;; = (snd-compose (scale-srate sound s) ((inverse f) - d)) -;; = (snd-compose (scale-srate sound s) (inverse f(t + d))) -;; = (snd-compose (scale-srate sound s) (inverse (shift f -d))) -;; snd-inverse takes a time and sample rate. For time, use zero. -;; The sample rate of inverse determines the final sample rate of -;; this function, so use *SOUND-SRATE*: -;; = (snd-compose (scale-srate sound s) (snd-inverse (shift-time f (- d)) -;; 0 *SOUND-SRATE*)) -;; -(defun nyq:sound (sound) - (cond ((null (warp-function *WARP*)) - (snd-xform sound (/ (snd-srate sound) (warp-stretch *WARP*)) - (local-to-global 0) - *START* *STOP* (db-to-linear (get-loud)))) - (t - (snd-compose (scale-srate sound (warp-stretch *WARP*)) - (snd-inverse (shift-time (warp-function *WARP*) - (- (warp-time *WARP*))) - 0 *SOUND-SRATE*))))) - -(defun nyq:sound-of-array (sound) - (let* ((n (length sound)) - (s (make-array n))) - (dotimes (i n) - (setf (aref s i) (nyq:sound (aref sound i)))) - s)) - - -(defun sound (sound) - (ny:typecheck (not (or (soundp sound) (multichannel-soundp sound))) - (ny:error "SOUND" 0 '((SOUND) nil) sound t)) - (cond ((arrayp sound) - (nyq:sound-of-array sound)) - (t - (nyq:sound sound)))) - - -;; (SCALE-SRATE SOUND SCALE) -;; multiplies the sample rate by scale -(defun scale-srate (sound scale) - (ny:typecheck (not (soundp sound)) - (ny:error "SCALE-SRATE" 1 '((SOUND) "sound") sound)) - (ny:typecheck (not (numberp scale)) - (ny:error "SCALE-SRATE" 2 '((NUMBER) "scale") scale)) - (let ((new-srate (* scale (snd-srate sound)))) - (snd-xform sound new-srate (snd-time sound) - MIN-START-TIME MAX-STOP-TIME 1.0))) - - -;; (SHIFT-TIME SOUND SHIFT) -;; shift the time of a function by SHIFT, i.e. if SOUND is f(t), -;; then (shift-time SOUND SHIFT) is f(t - SHIFT). Note that if -;; you look at plots, the shifted sound will move *right* when SHIFT -;; is positive. -(defun shift-time (sound shift) - (ny:typecheck (not (soundp sound)) - (ny:error "SHIFT-TIME" 1 '((SOUND) "sound") sound)) - (ny:typecheck (not (numberp shift)) - (ny:error "SHIFT-TIME" 2 '((NUMBER) "shift") shift)) - (snd-xform sound (snd-srate sound) (+ (snd-t0 sound) shift) - MIN-START-TIME MAX-STOP-TIME 1.0)) - - -;; (control sound) -;; Same as (sound sound), except this is used for control signals. -;; This code is identical to sound. -(defun control (sound) - (ny:typecheck (not (or (soundp sound) (multichannel-soundp sound))) - (ny:error "CONTROL" 0 '((SOUND) nil) sound t)) - (cond ((arrayp sound) - (nyq:sound-of-array sound)) - (t - (nyq:sound sound)))) - - -;; (cue-file string) -;; Loads a sound file with the given name, returning a sound which is -;; transformed to the current environment. -(defun cue-file (name) - (ny:typecheck (not (stringp name)) - (ny:error "CUE-FILE" 0 '((STRING) "name") name)) - (cue (force-srate *SOUND-SRATE* (s-read name)))) - - -;; (env t1 t2 t4 l1 l2 l3 &optional duration) -;; Creates a 4-phase envelope. -;; tN is the duration of phase N, and lN is the final level of -;; phase N. t3 is implied by the duration, and l4 is 0.0. -;; If dur is not supplied, then 1.0 is assumed. The envelope -;; duration is the product of dur, *STRETCH*, and *SUSTAIN*. If -;; t1 + t2 + 2ms + t4 > duration, then a two-phase envelope is -;; substituted that has an attack/release time ratio = t1/t4. -;; The sample rate of the returned sound is *CONTROL-SRATE*. -;; -;; Time transformation: the envelope is not warped; the start time and -;; stop times are warped to global time. Then the value of *SUSTAIN* at -;; the beginning of the envelope is used to determining absolute duration. -;; Since PWL is ultimately called to create the envelope, we must use -;; ABS-ENV to prevent any further transforms inside PWL. We use -;; (AT global-start ...) inside ABS-ENV so that the final result has -;; the proper starting time. -;; -(defun env (t1 t2 t4 l1 l2 l3 &optional (duration 1.0)) - (ny:typecheck (not (and (numberp t1) (numberp t2) (numberp t4) - (numberp l1) (numberp l2) (numberp l3))) - (error "In ENV, expected 6 numbers (t1, t2, t4, l1, l2, l3)" - (list t1 t2 t4 l1 l2 l3))) - (ny:typecheck (not (numberp duration)) - (ny:error "ENV" 7 '((NUMBER) "duration") duration)) - (let (actual-dur min-dur ratio t3 - (actual-dur (get-duration duration))) - (setf min-dur (+ t1 t2 t4 0.002)) - (cond ((< actual-dur min-dur) - (setf ratio (/ t1 (float (+ t1 t4)))) - (setf t1 (* ratio actual-dur)) - (setf t2 (- actual-dur t1)) - (setf t3 0.0) - (setf t4 0.0) - (setf l2 0.0) - (setf l3 0.0)) - (t - (setf t3 (- actual-dur t1 t2 t4)))) - (ny:set-logical-stop - (abs-env (at *rslt* - (pwl t1 l1 (+ t1 t2) l2 (- actual-dur t4) l3 actual-dur))) - duration))) - - -(defun to-mono (sound) - (ny:typecheck (not (or (soundp sound) (multichannel-soundp sound))) - (ny:error "TO-MONO" 1 '((SOUND) NIL) sound t)) - (let ((s sound)) - (cond ((arrayp sound) - (setf s (aref sound 0)) ;; ANY channel opens the gate - (dotimes (i (1- (length sound))) - (setf s (nyq:add-2-sounds s (aref sound (1+ i))))))) - s)) - - -(defun gate (sound lookahead risetime falltime floor threshold - &optional (source "GATE")) - ;(ny:typecheck (not (soundp sound)) - (ny:typecheck (not (or (soundp sound) (multichannel-soundp sound))) - (ny:error source 1 '((SOUND) "sound") sound t)) - (ny:typecheck (not (numberp lookahead)) - (ny:error source 2 '((NUMBER) "lookahead") lookahead)) - (ny:typecheck (not (numberp risetime)) - (ny:error source 3 '((NUMBER) "risetime") risetime)) - (ny:typecheck (not (numberp falltime)) - (ny:error source 4 '((NUMBER) "falltime") falltime)) - (ny:typecheck (not (numberp floor)) - (ny:error source 5 '((NUMBER) "floor") floor)) - (ny:typecheck (not (numberp threshold)) - (ny:error source 6 '((NUMBER) "threshold") threshold)) - (cond ((< lookahead risetime) - (format t "WARNING: lookahead (~A) ~A (~A) in ~A ~A ~A.\n" - lookahead "must be greater than risetime" risetime - source "function; setting lookahead to" risetime) - (setf lookahead risetime))) - (cond ((< risetime 0) - (format t "WARNING: risetime (~A) ~A ~A ~A\n" risetime - "must be greater than zero in" source - "function; setting risetime to 0.01.") - (setf risetime 0.01))) - (cond ((< falltime 0) - (format t "WARNING: ~A ~A function; setting falltime to 0.01.\n" - "falltime must be greater than zero in" source) - (setf falltime 0.01))) - (cond ((< floor 0.00001) - (format t "WARNING: ~A ~A function; setting floor to 0.00001.\n" - "floor must be greater than zero in" source) - (setf floor 0.00001))) - (let (s) ;; s becomes sound after collapsing to one channel - (cond ((arrayp sound) ;; use s-max over all channels so that - (setf s (aref sound 0)) ;; ANY channel opens the gate - (dotimes (i (1- (length sound))) - (setf s (s-max s (aref sound (1+ i)))))) - (t (setf s sound))) - (setf s (snd-gate (seq (cue s) - (stretch-abs 1.0 (s-rest lookahead))) - lookahead risetime falltime floor threshold)) - ;; snd-gate delays everything by lookahead, so this will slide the sound - ;; earlier by lookahead and delete the first lookahead samples - (prog1 (snd-xform s (snd-srate s) (snd-t0 s) - (+ (snd-t0 s) lookahead) MAX-STOP-TIME 1.0) - ;; This is *really* tricky. Normally, we would return now and - ;; the GC would free s and sound which are local variables. The - ;; only references to the sounds once stored in s and sound are - ;; lazy unit generators that will free samples almost as soon as - ;; they are computed, so no samples will accumulate. But wait! The - ;; 2nd SEQ expression with S-REST can reference s and sound because - ;; (due to macro magic) a closure is constructed to hold them until - ;; the 2nd SEQ expression is evaluated. It's almost as though s and - ;; sound are back to being global variables. Since the closure does - ;; not actually use either s or sound, we can clear them (we are - ;; still in the same environment as the closures packed inside SEQ, - ;; so s and sound here are still the same variables as the ones in - ;; the closure. Note that the other uses of s and sound already made - ;; copies of the sounds, and s and sound are merely references to - ;; them -- setting to nil will not alter the immutable lazy sound - ;; we are returning. Whew! - (setf s nil) (setf sound nil)))) - - -;; (osc-note step &optional duration env sust volume sound) -;; Creates a note using table-lookup osc, but with an envelope. -;; The ENV parameter may be a parameter list for the env function, -;; or it may be a sound. -;; -(defun osc-note (pitch &optional (duration 1.0) - (env-spec '(0.02 0.1 0.3 1.0 .8 .7)) - (volume 0.0) - (table *TABLE*)) - (ny:typecheck (not (numberp pitch)) - (ny:error "OSC-NOTE" 1 '((STEP) "pitch") pitch)) - (ny:typecheck (not (numberp duration)) - (ny:error "OSC-NOTE" 2 '((NUMBER) "duration") duration)) - (ny:assert-env-spec env-spec - "In OSCNOTE, 3rd argument (env-spec) must be a list of 6 or 7 numbers to pass as arguments to ENV") - (ny:typecheck (not (numberp volume)) - (ny:error "OSC-NOTE" 4 '((NUMBER) "volume") volume)) - (ny:assert-table "OSC-NOTE" 5 "table" table) - - (ny:set-logical-stop - (mult (loud volume (osc pitch duration table)) - (if (listp env-spec) - (apply 'env env-spec) - env-spec)) - duration)) - - -;; force-srate -- resample snd if necessary to get sample rate -; -(defun force-srate (sr snd) - (ny:typecheck (not (numberp sr)) - (ny:error "FORCE-SRATE" 1 '((NUMBER) "sr") sr)) - (ny:typecheck (not (or (soundp snd) (multichannel-soundp snd))) - (ny:error "FORCE-SRATE" 2 '((SOUND) "snd") snd t)) - (cond ((arrayp snd) - (let* ((len (length snd)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) - (force-srate sr (aref snd i)))) - result)) - (t - (let ((snd-sr (snd-srate snd))) - (cond ((> sr snd-sr) (snd-up sr snd)) - ((< sr snd-sr) (snd-down sr snd)) - (t snd)))))) - - -(defun force-srates (srs snd) - (cond ((and (numberp srs) (soundp snd)) - (force-srate srs snd)) - ((and (arrayp srs) (arrayp snd)) - (let* ((len (length snd)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) - (force-srate (aref srs i) (aref snd i)))) - result)) - (t (error (format nil "In force-srates: arguments not compatible. srs is ~A, snd is ~A. Perhaps you are constructing a sequence using both mono and multi-channel sounds." - (type-of srs) (type-of snd)))))) - - -;; (breakpoints-convert (t1 x1 t2 x2 ... tn) t0) -;; converts times to sample numbers and scales amplitudes -;; t0 is the global (after warping) start time -;; -;; input list is one or more numbers -;; result is abs-sample-count, val, abs-sample-count, val, ... -;; if the list length is odd, the result length is odd, and -;; snd-pwl treats it as if a final value of zero was appended -;; -;; NOTE: there were some stack overflow problems with the original -;; recursive version (in comments now), so it was rewritten as an -;; iteration. -;; -(defun breakpoints-convert (list t0 source) - (prog (sample-count result sust (last-count 0)) - (setf sust (get-sustain)) - (ny:typecheck (not (consp list)) - (error (format nil "In ~A, expected a list of numbers" source) list)) - loop - (ny:typecheck (not (numberp (car list))) - (error (format nil "In ~A, expected only numbers in breakpoint list, got ~A" - source (car list)))) - (setf sample-count - (truncate (+ 0.5 (* (- (local-to-global (* (car list) sust)) t0) - *control-srate*)))) - ; now we have a new sample count to put into result list - ; make sure result is non-decreasing - (cond ((< sample-count last-count) - (setf sample-count last-count))) - (setf last-count sample-count) - (push sample-count result) - (cond ((cdr list) - (setf list (cdr list)) - (ny:typecheck (not (numberp (car list))) - (error (format nil "In ~A, expected only numbers in breakpoint list" source) - (car list))) - (push (float (car list)) result))) - (setf list (cdr list)) - (cond (list - (go loop))) - (return (reverse result)))) - - -;; (pwl t1 l1 t2 l2 ... tn) -;; Creates a piece-wise linear envelope from breakpoint data. -;; -(defun pwl (&rest breakpoints) (pwl-list breakpoints "PWL")) - -(defun pwlr (&rest breakpoints) (pwlr-list breakpoints "PWLR")) - -;; BREAKPOINTS-RELATIVE list source -;; converts list, which has the form (value dur value dur value ...) -;; into the form (value time value time value ...) -;; the list may have an even or odd length -;; -(defun breakpoints-relative (breakpoints source) - (prog (result (sum 0.0)) - (ny:typecheck (not (consp breakpoints)) - (error (format nil "In ~A, expected list of numbers, got ~A" - source breakpoints))) - loop - (ny:typecheck (not (numberp (car breakpoints))) - (error (format nil - "In ~A, expected only numbers in breakpoints list, got ~A" - source (car breakpoints)))) - (setf sum (+ sum (car breakpoints))) - (push sum result) - (cond ((cdr breakpoints) - (setf breakpoints (cdr breakpoints)) - (ny:typecheck (not (numberp (car breakpoints))) - (error (format nil - "In ~A, expected only numbers in breakpoints list, got ~A" - source (car breakpoints)))) - (push (car breakpoints) result))) - (setf breakpoints (cdr breakpoints)) - (cond (breakpoints - (go loop))) - (return (reverse result)))) - - -(defun pwlr-list (breakpoints &optional (source "PWLR-LIST")) - (pwl-list (breakpoints-relative breakpoints source) source)) - -(defun pwl-list (breakpoints &optional (source "PWL-LIST")) - (let ((t0 (local-to-global 0))) - (snd-pwl t0 *control-srate* (breakpoints-convert breakpoints t0 source)))) - -;; (pwlv l1 t1 l2 t2 ... ln) -;; Creates a piece-wise linear envelope from breakpoint data; -;; the function initial and final values are explicit -;; -(defun pwlv (&rest breakpoints) - ;use pwl, modify breakpoints with initial and final changes - ;need to put initial time of 0, and final time of 0 - (pwlv-list breakpoints "PWLV")) - -(defun pwlv-list (breakpoints &optional (source "PWLV-LIST")) - (ny:typecheck (not (consp breakpoints)) - (error (format nil "In ~A, expected list of numbers, got ~A" - source breakpoints))) - (pwl-list (cons 0.0 breakpoints) source)) - -(defun pwlvr (&rest breakpoints) (pwlvr-list breakpoints "PWLVR")) - -(defun pwlvr-list (breakpoints &optional (source "PWLVR-LIST")) - (ny:typecheck (not (consp breakpoints)) - (error (format nil "In ~A, expected list of numbers, got ~A" - source breakpoints))) - (pwlr-list (cons 0.0 breakpoints) source)) - -(defun pwe (&rest breakpoints) - (pwe-list breakpoints "PWE")) - -(defun pwe-list (breakpoints &optional (source "PWE-LIST")) - (ny:typecheck (not (consp breakpoints)) - (error (format nil "In ~A, expected list of numbers, got ~A" - source breakpoints))) - (pwev-list (cons 1.0 breakpoints) source)) - -(defun pwer (&rest breakpoints) - (pwer-list breakpoints "PWER")) - -(defun pwer-list (breakpoints &optional (source "PWER-LIST")) - (pwe-list (breakpoints-relative breakpoints source) source)) - -(defun pwev (&rest breakpoints) - (pwev-list breakpoints "PWEV")) - -(defun pwev-list (breakpoints &optional (source "PWEV-LIST")) - (let ((lis (breakpoints-log breakpoints source))) - (s-exp (pwl-list lis)))) - -(defun pwevr (&rest breakpoints) (pwevr-list breakpoints "PWEVR")) - -(defun pwevr-list (breakpoints &optional (source "PWEVR-LIST")) - (ny:typecheck (not (consp breakpoints)) - (error (format nil "In ~A, expected list of numbers, got ~A" - source breakpoints))) - (pwev-list (cdr (breakpoints-relative (cons 0.0 breakpoints) source)) source)) - - -;; input is 2 or more numbers representing val, time, val, time, ... -;; output is odd number of 1 or more numbers representing -;; time, val, time, val, ..., time -;; -;; -(defun breakpoints-log (breakpoints source) - (prog ((result '(0.0)) val tim) -loop - (ny:typecheck (not (consp breakpoints)) - (error (format nil "In ~A, expected list of numbers, got ~A" - source breakpoints))) - (ny:typecheck (not (numberp (car breakpoints))) - (error (format nil "In ~A, expected number in breakpoint list, got ~A" - source (car breakpoints)))) - - (setf val (float (car breakpoints))) - (setf breakpoints (cdr breakpoints)) - - (cond (breakpoints - (ny:typecheck (not (consp breakpoints)) - (error (format nil "In ~A, expected list of numbers, got ~A" - source (car breakpoints)))) - (setf tim (car breakpoints)) - (setf breakpoints (cdr breakpoints)) - (ny:typecheck (not (numberp tim)) - (error (format nil "In ~A, expected number in breakpoint list, got ~A" - source tim))))) - - (setf result (cons tim (cons (log val) result))) - (cond ((null breakpoints) - (return (reverse result)))) - (go loop))) - - -;; SOUND-WARP -- apply warp function to a sound -;; -(defun sound-warp (warp-fn signal &optional wrate) - (ny:typecheck (not (soundp warp-fn)) - (ny:error "SOUND-WARP" 1 '((SOUND) "warp-fn") warp-fn)) - (ny:typecheck (not (soundp signal)) - (ny:error "SOUND-WARP" 2 '((SOUND) "signal") signal)) - (cond (wrate - (ny:typecheck (not (numberp wrate)) - (ny:error "SOUND-WARP" 3 '((NUMBER) "wrate") wrate)) - (snd-resamplev signal *sound-srate* - (snd-inverse warp-fn (local-to-global 0) wrate))) - (t - (snd-compose signal - (snd-inverse warp-fn (local-to-global 0) *sound-srate*))))) - -(defun snd-extent (sound maxsamples) - (ny:typecheck (not (soundp sound)) - (ny:error "SND-EXTENT" 1 '((SOUND) "sound") sound)) - (ny:typecheck (not (integerp maxsamples)) - (ny:error "SND-EXTENT" 2 '((INTEGER) "maxsamples") maxsamples)) - (list (snd-t0 sound) - (+ (snd-t0 sound) (/ (snd-length sound maxsamples) - (snd-srate sound))))) - -(setfn snd-flatten snd-length) - -;; (maketable sound) -;; Creates a table for osc, lfo, etc. by assuming that the samples -;; in sound represent one period. The sound must start at time 0. - -(defun maketable (sound) - (ny:typecheck (not (soundp sound)) - (ny:error "MAKETABLE" 0 '((SOUND) nil) sound)) - (list sound - (hz-to-step - (/ 1.0 - (cadr (snd-extent sound 1000000)))) - T)) - - -; simple stereo pan: as where goes from 0 to 1, sound -; is linearly panned from left to right -; -(defun pan (sound where) - (ny:typecheck (not (soundp sound)) - (ny:error "PAN" 1 '((SOUND) "sound") sound)) - (ny:typecheck (not (or (soundp where) (numberp where))) - (ny:error "PAN" 2 '((NUMBER SOUND) "where") where)) - (vector (mult sound (sum 1 (mult -1 where))) - (mult sound where))) - - -(setf prod-source "PROD (or * in SAL)") - -(defun prod (&rest snds) - (cond ((null snds) - (snd-zero (local-to-global 0) *sound-srate*)) - ((null (cdr snds)) - (car snds)) - ((null (cddr snds)) - (nyq:prod2 (car snds) (cadr snds) prod-source)) - (t - (nyq:prod2 (car snds) (apply #'prod (cdr snds)) prod-source)))) - -(setfn mult prod) - - -;; (NYQ:PROD-OF-ARRAYS S1 S2) - form pairwise products -; -(defun nyq:prod-of-arrays (s1 s2 source) - (let* ((n (length s1)) - (p (make-array n))) - (ny:typecheck (/= n (length s2)) - (error (strcat "In " source ", unequal number of channels, got " - (param-to-string s1) " and " (param-to-string s2)))) - (dotimes (i n) - (setf (aref p i) (nyq:prod2 (aref s1 i) (aref s2 i) source))) - p)) - - -; nyq:prod2 - multiply two arguments -; -(defun nyq:prod2 (s1 s2 source) - (setf s1 (nyq:coerce-to s1 s2)) - (setf s2 (nyq:coerce-to s2 s1)) - (cond ((arrayp s1) - (nyq:prod-of-arrays s1 s2 source)) - (t - (nyq:prod-2-sounds s1 s2 source)))) - - -; (PROD-2-SOUNDS S1 S2) - multiply two sound arguments -; -(defun nyq:prod-2-sounds (s1 s2 source) - (cond ((numberp s1) - (cond ((numberp s2) - (* s1 s2)) - ((soundp s2) - (snd-scale s1 s2)) - (t - (ny:error source 0 number-sound-anon s2 t)))) - ((numberp s2) - (ny:typecheck (not (soundp s1)) - (ny:error source 0 number-sound-anon s1 t)) - (snd-scale s2 s1)) - ((and (soundp s1) (soundp s2)) - (snd-prod s1 s2)) - ((soundp s1) - (ny:error source 0 number-sound-anon s2 t)) - (t - (ny:error source 0 number-sound-anon s1 t)))) - - -;; RAMP -- linear ramp from 0 to x -;; -(defun ramp (&optional (x 1)) - (ny:typecheck (not (numberp x)) - (ny:error "RAMP" 0 number-anon x)) - (let* ((duration (get-duration x))) - (ny:set-logical-stop - (warp-abs nil - (at *rslt* - (sustain-abs 1 - (pwl duration 1 (+ duration (/ *control-srate*)))))) - x))) - - -(defun resample (snd rate) - (ny:typecheck (not (or (soundp snd) (multichannel-soundp snd))) - (ny:error "RESAMPLE" 1 '((SOUND) nil) snd t)) - (ny:typecheck (not (numberp rate)) - (ny:error "RESAMPLE" 2 '((NUMBER) "rate") rate)) - (cond ((arrayp snd) - (let* ((len (length snd)) - (result (make-array len))) - (dotimes (i len) - (setf (aref result i) - (snd-resample (aref snd i) rate))) - result)) - (t - (snd-resample snd rate)))) - - -(defun scale (amt snd) - (multichan-expand "SCALE" #'snd-scale - '(((NUMBER) "amt") ((SOUND) "snd")) amt snd)) - - -(setfn s-print-tree snd-print-tree) - - -;; (PEAK sound-expression number-of-samples) - find peak amplitude -; -; NOTE: this used to be called s-max -; It is tempting to try using multichan-expand here to get peaks -; from multichannel sounds, but at this point the argument is just -; an expression, so we cannot tell if it is multichannel. We could -; evaluate the expression, but then we'd have a local binding and -; would retain samples in memory if we called snd-max on each channel. -; -(defmacro peak (expression maxlen) - `(snd-max ',expression ,maxlen)) - - -;; (S-MAX S1 S2) - return maximum of S1, S2 -; -(defun s-max (s1 s2) - (setf s1 (nyq:coerce-to s1 s2)) - (setf s2 (nyq:coerce-to s2 s1)) - (cond ((arrayp s1) - (nyq:max-of-arrays s1 s2)) - (t - (nyq:max-2-sounds s1 s2)))) - -(defun nyq:max-of-arrays (s1 s2) - (let* ((n (length s1)) - (p (make-array n))) - (ny:typecheck (/= n (length s2)) - (error (strcat "In S-MAX, unequal number of channels, got " - (param-to-string s1) " and " (param-to-string s2)))) - (dotimes (i n) - (setf (aref p i) (s-max (aref s1 i) (aref s2 i)))) - p)) - -(defun nyq:max-2-sounds (s1 s2) - (cond ((numberp s1) - (cond ((numberp s2) - (max s1 s2)) - ((soundp s2) - (snd-maxv s2 - (snd-const s1 (local-to-global 0.0) - (snd-srate s2) (get-duration 1.0)))) - (t - (ny:error "S-MAX" 2 number-sound-anon s2 t)))) - ((numberp s2) - (ny:typecheck (not (soundp s1)) - (ny:error "S-MAX" 2 number-sound-anon s2 t)) - (snd-maxv s1 (snd-const s2 (local-to-global 0.0) - (snd-srate s1) (get-duration 1.0)))) - ((and (soundp s1) (soundp s2)) - (snd-maxv s1 s2)) - ((soundp s1) - (ny:error "S-MAX" 2 number-sound-anon s2 t)) - (t - (ny:error "S-MAX" 1 number-sound-anon s1 t)))) - - -(defun s-min (s1 s2) - (setf s1 (nyq:coerce-to s1 s2)) - (setf s2 (nyq:coerce-to s2 s1)) - (cond ((arrayp s1) - (nyq:min-of-arrays s1 s2)) - (t - (nyq:min-2-sounds s1 s2)))) - -(defun nyq:min-of-arrays (s1 s2) - (let* ((n (length s1)) - (p (make-array n))) - (ny:typecheck (/= n (length s2)) - (error (strcat "In S-MIN, unequal number of channels, got " - (param-to-string s1) (param-to-string s2)))) - (cond ((/= n (length s2)) - (error "unequal number of channels in max"))) - (dotimes (i n) - (setf (aref p i) (s-min (aref s1 i) (aref s2 i)))) - p)) - -(defun nyq:min-2-sounds (s1 s2) - (cond ((numberp s1) - (cond ((numberp s2) - (min s1 s2)) - ((soundp s2) - (snd-minv s2 - (snd-const s1 (local-to-global 0.0) - (snd-srate s2) (get-duration 1.0)))) - (t - (ny:error "S-MIN" 2 number-sound-anon s2 t)))) - ((numberp s2) - (ny:typecheck (not (soundp s1)) - (ny:error "S-MIN" 2 number-sound-anon s2 t)) - (snd-minv s1 (snd-const s2 (local-to-global 0.0) - (snd-srate s1) (get-duration 1.0)))) - ((and (soundp s1) (soundp s2)) - (snd-minv s1 s2)) - ((soundp s1) - (ny:error "S-MIN" 2 number-sound-anon s2 t)) - (t - (ny:error "S-MIN" 1 number-sound-anon s1 t)))) - - -(defun snd-minv (s1 s2) - (snd-scale -1.0 (snd-maxv (snd-scale -1.0 s1) (snd-scale -1.0 s2)))) - -; sequence macros SEQ and SEQREP are now in seq.lsp: -; -(load "seq" :verbose NIL) - - -; set-logical-stop - modify the sound and return it, time is shifted and -; stretched -(defun set-logical-stop (snd tim) - (ny:typecheck (not (numberp tim)) - (ny:error "SET-LOGICAL-STOP" 2 '((NUMBER) "logical stop time") tim)) - (ny:typecheck (not (or (soundp snd) (multichannel-soundp snd))) - (ny:error "SET-LOGICAL-STOP" 1 '((SOUND) "snd") snd t)) - (multichan-expand "SET-LOGICAL-STOP" #'ny:set-logical-stop - '(((SOUND) "snd") ((NUMBER) "logical stop time")) snd tim)) - - -;; NY:SET-LOGICAL-STOP - "fast" set-logical-stop: no typechecks and no -;; multichannel expansion -(defun ny:set-logical-stop (snd tim) - (let ((d (local-to-global tim))) - (snd-set-logical-stop snd d) - snd)) - - -; SET-LOGICAL-STOP-ABS - modify the sound and return it -; -(defun set-logical-stop-abs (snd tim) - (ny:typecheck (not (numberp tim)) - (ny:error "SET-LOGICAL-STOP-ABS" 2 '((NUMBER) "logical stop time") tim)) - (ny:typecheck (not (or (soundp snd) (multichannel-soundp snd))) - (ny:error "SET-LOGICAL-STOP-ABS" 1 '((SOUND) "snd") snd t)) - (multichan-expand "SET-LOGICAL-STOP-ABS" #'ny:set-logical-stop-abs - '(((SOUND) "snd") ((NUMBER) "logical stop time")) snd tim)) - - -(defun ny:set-logical-stop-abs (snd tim) - (snd-set-logical-stop snd tim) - snd) - - -(defmacro simrep (pair sound) - `(let (_snds) - (dotimes ,pair (push ,sound _snds)) - (sim-list _snds "SIMREP"))) - -(defun sim (&rest snds) - (sim-list snds "SIM or SUM (or + in SAL)")) - -(setfn sum sim) - -(defun sim-list (snds source) - (let (a b) - (cond ((null snds) - (snd-zero (local-to-global 0) *sound-srate*)) - ((null (cdr snds)) - (setf a (car snds)) - (ny:typecheck (not (or (numberp a) (soundp a) (multichannel-soundp a))) - (ny:error source 0 number-sound-anon a t)) - a) - ((null (cddr snds)) - ;; sal-plus does typechecking, then calls nyq:add2 - (sal-plus (car snds) (cadr snds))) - (t - (setf a (car snds)) - (ny:typecheck (not (or (numberp a) (soundp a) (multichannel-soundp a))) - (ny:error source 0 number-sound-anon a t)) - (nyq:add2 a (sim-list (cdr snds) source)))))) - - -(defun s-rest (&optional (dur 1.0) (chans 1)) - (ny:typecheck (not (numberp dur)) - (ny:error "S-REST" 1 '((NUMBER) "dur") dur)) - (ny:typecheck (not (integerp chans)) - (ny:error "S-REST" 2 '((INTEGER) "chans") chans)) - (let ((d (get-duration dur)) - r) - (cond ((= chans 1) - (snd-const 0.0 *rslt* *SOUND-SRATE* d)) - (t - (setf r (make-array chans)) - (dotimes (i chans) - (setf (aref r i) (snd-const 0.0 *rslt* *SOUND-SRATE* d))) - r)))) - - -(defun tempo (warpfn) - (ny:typecheck (not (soundp warpfn)) - (ny:error "TEMPO" 0 '((SOUND) "warpfn") warpfn)) - (slope (snd-inverse warpfn (local-to-global 0) *control-srate*))) - - -;; (SUM-OF-ARRAYS S1 S2) - add multichannel sounds -; -; assumes s1 & s2 are arrays of numbers and sounds -; -; result has as many channels the largest of s1, s2 -; corresponding channels are added, extras are copied -; -(defun sum-of-arrays (s1 s2) -; (ny:typecheck (not (multichannel-soundp s1)) -; (error (strcat "In SUM or SIM (or + in SAL), at least one channel in the array contains a non-sound, got " (param-to-string s1)))) -; (ny:typecheck (not (multichannel-soundp s2)) -; (error (strcat "In SUM or SIM (or + in SAL), at least one channel in the array contains a non-sound, got " (param-to-string s2)))) - (let* ((n1 (length s1)) - (n2 (length s2)) - (n (min n1 n2)) - (m (max n1 n2)) - (result (make-array m)) - (big-s (if (> n1 n2) s1 s2)) - v1 v2) - - (dotimes (i n) - (setf v1 (aref s1 i) v2 (aref s2 i)) - (setf (aref result i) - (cond ((numberp v1) - (if (numberp v2) (+ v1 v2) (snd-offset v2 v1))) - ((numberp v2) - (if (numberp v1) (+ v1 v2) (snd-offset v1 v2))) - (t - (nyq:add-2-sounds v1 v2))))) - (dotimes (i (- m n)) - (setf (aref result (+ n i)) (aref big-s (+ n i)))) - result)) - - -;; (WARP fn behavior) - warp behavior according to fn -;; -;; fn is a map from behavior time to local time, and *WARP* expresses -;; a map from local to global time. -;; To produce a new *WARP* for the environment, we want to compose the -;; effect of the current *WARP* with fn. Note that fn is also a behavior. -;; It is evaluated in the current environment first, then it is used to -;; modify the environment seen by behavior. -;; *WARP* is a triple: (d s f) denoting the function f(st+d). -;; Letting g represent the new warp function fn, we want f(st+d) o g, or -;; f(s*g(t) + d) in the form (d' s' f'). -;; Let's do this one step at a time: -;; f(s*g(t) + d) = f(scale(s, g) + d) -;; = (shift f -d)(scale(s, g)) -;; = (snd-compose (shift-time f (- d)) (scale s g)) -;; -;; If f in NIL, it denotes the identity mapping f(t)=t, so we can -;; simplify: -;; f(scale(s, g) + d) = scale(s, g) + d -;; = (snd-offset (scale s g) d) - -(defmacro warp (x s) - `(progv '(*WARP*) - (let ((wp ,x)) - (list (list 0.0 1.0 - (cond ((warp-function *WARP*) - (ny:typecheck (not (soundp wp)) - (ny:error "WARP" 1 '((SOUND) "warp function") wp)) - (snd-compose (shift-time (warp-function *WARP*) - (- (warp-time *WARP*))) - (snd-scale (warp-stretch *WARP*) wp))) - (t - (ny:typecheck (not (soundp wp)) - (ny:error "WARP" 1 '((SOUND) "warp function") wp)) - (snd-offset (snd-scale (warp-stretch *WARP*) wp) - (warp-time *WARP*))))))) - ,s)) - - -(defmacro warp-abs (x s) - `(progv '(*WARP*) - (let ((wp ,x)) - (ny:typecheck (and wp (not (soundp wp))) - (ny:error "WARP-ABS" 1 '((NULL SOUND) NIL) wp)) - (list (list 0.0 1.0 wp))) - ,s)) - - -;; MULTICHAN-EXPAND -- construct and return array according to args -;; -;; arrays are used in Nyquist to represent multiple channels -;; if any argument is an array, make sure all array arguments -;; have the same length. Then, construct a multichannel result -;; by calling fn once for each channel. The arguments passed to -;; fn for the i'th channel are either the i'th element of an array -;; argument, or just a copy of a non-array argument. -;; -;; types should be a list of type info for each arg, where type info is: -;; (arg1-info arg2-info ...), where each arg-info is -;; (valid-type-list name-or-nil), where valid-type-list is a list -;; of valid types from among NUMBER, SOUND, POSITIVE (number > 0), -;; NONNEGATIVE (number >= 0), INTEGER, STEP, STRING, -;; POSITIVE-OR_NULL (a positive number or nil), -;; INT-OR-NULL (integer or nil), or NULL (the value can be nil). -;; It is implied that arrays of these are valid too. name-or-nil -;; is the parameter name as a string if the parameter name should -;; be printed, or NIL if the parameter name should not be printed. -;; There can be at most 2 elements in valid-type-list, and if -;; there are 2 elements, the 2nd one must be SOUND. For example, -;; arg-info '((NUMBER SOUND) "cutoff") might generate the error -;; In LOPASS8, 2nd argument (cutoff) must be a number, sound -;; or array thereof, got "bad-value" -;; -;; Many existing Nyquist plug-ins require the old version of multichan-expand, -;; so in Audacity we need to support both the old and new versions. -(defun multichan-expand (&rest args) - (if (stringp (first args)) - (apply 'multichan-expand-new args) - (apply 'multichan-expand-old args))) - -;; Legacy version: -(defun multichan-expand-old (fn &rest args) - (let (len newlen result) ; len is a flag as well as a count - (dolist (a args) - (cond ((arrayp a) - (setf newlen (length a)) - (cond ((and len (/= len newlen)) - (error (format nil "In ~A, two arguments are vectors of differing length." fn)))) - (setf len newlen)))) - (cond (len - (setf result (make-array len)) - ; for each channel, call fn with args - (dotimes (i len) - (setf (aref result i) - (apply fn - (mapcar - #'(lambda (a) - ; take i'th entry or replicate: - (cond ((arrayp a) (aref a i)) - (t a))) - args)))) - result) - (t - (apply fn args))))) - -;; The new (Nyquist 3.15) version: -(defun multichan-expand-new (src fn types &rest args) - (let (chan len newlen result prev typ (index 0) nonsnd) - ; len is a flag as well as a count - (dolist (a args) - (setf typ (car types) types (cdr types)) - ;; we only report argument position when there is more than one. - ;; index tracks argument position, where 0 means no position to report - (if (> (length args) 1) (setf index (1+ index))) - (setf nonsnd (caar typ)) ;; if non-sound type allowed, what is it? - ;; compute the length of any array argument, and typecheck all of them - (cond ((arrayp a) - (setf newlen (length a)) - (ny:typecheck (and len (/= len newlen)) - (error (strcat "In " src - ", two arguments are multichannels of differing length, got " - (param-to-string prev) ", and " (param-to-string a)))) - (dotimes (i newlen) - (setf chan (aref a i)) - (cond ((and (eq nonsnd 'NUMBER) (numberp chan))) - ((and (member 'SOUND (car typ)) (soundp chan))) - ((and (eq nonsnd 'STEP) (numberp chan))) - ((and (eq nonsnd 'POSITIVE) (numberp chan) (> chan 0))) - ((and (eq nonsnd 'POSITIVE-OR-NULL) - (or (and (numberp chan) (> chan 0)) (null chan)))) - ((and (eq nonsnd 'NONNEGATIVE) (numberp chan) (>= chan 0))) - ((and (eq nonsnd 'INTEGER) (integerp chan))) - ((and (eq nonsnd 'STRING) (stringp chan))) - ((and (eq nonsnd 'NULL) (null chan))) - ((and (eq nonsnd 'INT-OR-NULL) - (or (integerp chan) (null chan)))) - (t (ny:error src index typ a t)))) - (setf prev a) - (setf len newlen)) - ((and (eq nonsnd 'NUMBER) (numberp a))) - ((and (member 'SOUND (car typ)) (soundp a))) - ((and (eq nonsnd 'STEP) (numberp a))) - ((and (eq nonsnd 'POSITIVE) (numberp a) (>= a 0))) - ((and (eq nonsnd 'POSITIVE-OR-NULL) - (or (and (numberp a) (> a 0)) (null a)))) - ((and (eq nonsnd 'NONNEGATIVE) (numberp a) (>= a 0))) - ((and (eq nonsnd 'INTEGER) (integerp a))) - ((and (eq nonsnd 'STRING) (stringp a))) - ((and (eq nonsnd 'NULL) (null a))) - ((and (eq nonsnd 'INT-OR-NULL) - (or (integerp a) (null a)))) - (t - (ny:error src index typ a t)))) - (cond (len - (setf result (make-array len)) - ; for each channel, call fn with args - (dotimes (i len) - (setf (aref result i) - (apply fn - (mapcar - #'(lambda (a) ; take i'th entry or replicate: - (cond ((arrayp a) (aref a i)) - (t a))) - args)))) - result) - (t - (apply fn args))))) - - -;; SELECT-IMPLEMENTATION-? -- apply an implementation according to args -;; -;; There is a different Nyquist primitive for each combination of -;; constant (NUMBERP) and time-variable (SOUNDP) arguments. E.g. -;; a filter with fixed parameters differs from one with varying -;; parameters. In most cases, the user just calls one function, -;; and the arguments are decoded here: - - -;; SELECT-IMPLEMENTATION-1-1 -- 1 sound arg, 1 selector -;; -(defun select-implementation-1-1 (source fns snd sel1 &rest others) - (ny:typecheck (not (soundp snd)) - (ny:error source 1 '((SOUND) nil) snd t)) - (cond ((numberp sel1) - (apply (aref fns 0) (cons snd (cons sel1 others)))) - ((soundp sel1) - (apply (aref fns 1) (cons snd (cons sel1 others)))) - (t - (ny:error source 2 number-sound-anon sel1 t)))) - - -;; SELECT-IMPLEMENTATION-1-2 -- 1 sound arg, 2 selectors -;; -;; choose implementation according to args 2 and 3. In this implementation, -;; since we have two arguments to test for types, we return from prog -;; if we find good types. That way, we can fall through the decision tree -;; and all paths lead to one call to ERROR if good types are not found. -;; -(defun select-implementation-1-2 (source fns snd sel1 sel2 &rest others) - (prog () - (ny:typecheck (not (soundp snd)) - (ny:error source 1 '((SOUND) nil) snd t)) - (cond ((numberp sel2) - (cond ((numberp sel1) - (return (apply (aref fns 0) - (cons snd (cons sel1 (cons sel2 others)))))) - ((soundp sel1) - (return (apply (aref fns 1) - (cons snd (cons sel1 (cons sel2 others)))))))) - ((soundp sel2) - (cond ((numberp sel1) - (return (apply (aref fns 2) - (cons snd (cons sel1 (cons sel2 others)))))) - ((soundp sel1) - (return (apply (aref fns 3) - (cons snd (cons sel1 (cons sel2 others))))))))) - (ny:typecheck (not (or (numberp sel1) (soundp sel1))) - (ny:error src 2 number-sound-anon sel1 t) - (ny:error src 3 number-sound-anon sel2 t)))) - - -;; some waveforms - -(setf *saw-table* (pwlvr -1 1 1)) ; eh, creepy way to get 2205 samples. -(setf *saw-table* (list *saw-table* (hz-to-step 1) T)) - -(setf *tri-table* (pwlvr -1 0.5 1 0.5 -1)) -(setf *tri-table* (list *tri-table* (hz-to-step 1) T)) - -(setf *id-shape* (pwlvr -1 2 1 .01 1)) ; identity - -(setf *step-shape* (seq (const -1) (const 1 1.01))) ; hard step at zero - -(defun exp-dec (hold halfdec length) - (ny:typecheck (not (numberp hold)) - (ny:error "EXP-DEC" 1 '((NUMBER) "hold") hold)) - (ny:typecheck (not (numberp halfdec)) - (ny:error "EXP-DEC" 2 '((NUMBER) "halfdec") halfdec)) - (ny:typecheck (not (numberp length)) - (ny:error "EXP-DEC" 3 '((NUMBER) "length") length)) - (let* ((target (expt 0.5 (/ length halfdec))) - (expenv (pwev 1 hold 1 length target))) - expenv) -) - -;;; operations on sounds - -(defun diff (x &rest y) (diff-list x y "DIFF (or - in SAL)")) - -(defun diff-list (x y source) - (cond ((and (numberp x) (numberp (car y)) (null (cdr y))) - (- x (car y))) ;; this is a fast path for the common case - (y (sal-plus x (nyq:prod2 -1 (car y) source) source)) - (t (nyq:prod2 -1 x source)))) - - -; compare-shape is a shape table -- origin 1. -(defun compare (x y &optional (compare-shape *step-shape*)) - (ny:typecheck (not (or (soundp x) (soundp y))) - (error "In COMPARE, either first or second argument must be a sound")) - (ny:typecheck (not (soundp compare-shape)) - (ny:error "COMPARE" 3 '((SOUND) "compare-shape") compare-shape)) - (ny:typecheck (not (or (soundp x) (numberp x))) - (ny:error "COMPARE" 1 '((SOUND NUMBER) nil) x)) - (ny:typecheck (not (or (soundp y) (numberp y))) - (ny:error "COMPARE" 2 '((SOUND NUMBER) nil) y)) - (let ((xydiff (diff-list x (list y) "COMPARE"))) - (shape xydiff compare-shape 1))) - -;;; oscs - -(defun osc-saw (hz) (hzosc hz *saw-table*)) -(defun osc-tri (hz) (hzosc hz *tri-table*)) - -; bias is [-1, 1] pulse width. sound or scalar. -; hz is a sound or scalar -(defun osc-pulse (hz bias &optional (compare-shape *step-shape*)) - (compare bias (osc-tri hz) compare-shape)) - -;;; tapped delays - -;(tapv snd offset vardelay maxdelay) -(defun tapv (snd offset vardelay maxdelay) - (multichan-expand "TAPV" #'snd-tapv - '(((SOUND) "snd") ((NUMBER) "offset") - ((SOUND) "vardelay") ((NUMBER) "maxdelay")) - snd offset vardelay maxdelay)) - -(defun tapf (snd offset vardelay maxdelay) - (multichan-expand "TAPF" #'snd-tapf - '(((SOUND) "snd") ((NUMBER) "offset") - ((SOUND) "vardelay") ((NUMBER) "maxdelay")) - snd offset vardelay maxdelay)) - - -;; autoload functions -- SELF-MODIFYING CODE! -;; generate functions that replace themselves by loading more files -;; and then re-calling themselves as if they were already loaded -;; -(defun autoload (filename &rest fns) - ;; filename is the file to load (a string) from the current path - ;; fns are symbols to be defined as function that will load filename - ;; the first time any one is called, and it is assumed that - ;; filename will define each function in fns, so the called - ;; function can be called again to execute the real implementation - (let ((cp (current-path))) - (cond ((string-equal cp "./") ;; this is the typical case - (setf cp (setdir ".")))) - ;; make sure cp ends in file separator - (cond ((not (equal (char cp (1- (length cp))) *file-separator*)) - (setf cp (strcat cp (string *file-separator*))))) - (setf cp (strcat cp filename)) - (dolist (fn fns) - (eval `(defun ,fn (&rest args) - (autoload-helper ,cp ',fn args)))))) - - -(defun autoload-helper (path fn args) - (if (abs-env (sal-load path)) - (apply fn args) - (error (strcat "Could not load " path)))) - - -(autoload "spec-plot.lsp" 'spec-plot) - -(autoload "spectral-analysis.lsp" 'sa-init) - diff --git a/Release/nyquist/printrec.lsp b/Release/nyquist/printrec.lsp deleted file mode 100644 index 4ca17bbc9d1ccce28c0cf97b13f93ad4b8e719ef..0000000000000000000000000000000000000000 --- a/Release/nyquist/printrec.lsp +++ /dev/null @@ -1,30 +0,0 @@ -; prints recursive list structure - -;(let (seen-list) -(setf seenlist nil) - (defun seenp (l) (member l seenlist :test 'eq)) - (defun make-seen (l) (setf seenlist (cons l seenlist))) - (defun printrec (l) (printrec-any l) (setf seenlist nil)) - (defun printrec-any (l) - (cond ((atom l) (prin1 l) (princ " ")) - ((seenp l) (princ "<...> ")) - (t - (make-seen l) - (princ "(") - (printrec-list l) - (princ ") "))) - nil) - (defun printrec-list (l) - (printrec-any (car l)) - (cond ((cdr l) - (cond ((seenp (cdr l)) - (princ "<...> ")) - ((atom (cdr l)) - (princ ". ") - (prin1 (cdr l)) - (princ " ")) - (t - (make-seen (cdr l)) - (printrec-list (cdr l)))))) - nil) -; ) diff --git a/Release/nyquist/profile.lsp b/Release/nyquist/profile.lsp deleted file mode 100644 index 0f7038b61db28abede72980faf2da4b45292b1da..0000000000000000000000000000000000000000 --- a/Release/nyquist/profile.lsp +++ /dev/null @@ -1,27 +0,0 @@ - -; profile.lsp -- support for profiling - -;## show-profile -- print profile data -(defun show-profile () - (let ((profile-flag (profile nil)) (total 0)) - (dolist (name *PROFILE*) - (setq total (+ total (get name '*PROFILE*)))) - (dolist (name *PROFILE*) - (format t "~A (~A%): ~A~%" - (get name '*PROFILE*) - (truncate - (+ 0.5 (/ (float (* 100 (get name '*PROFILE*))) - total))) - name)) - (format t "Total: ~A~%" total) - (profile profile-flag))) - - -;## start-profile -- clear old profile data and start profiling -(defun start-profile () - (profile nil) - (dolist (name *PROFILE*) - (remprop name '*PROFILE*)) - (setq *PROFILE* nil) - (profile t)) - diff --git a/Release/nyquist/rawwaves/mand1.raw b/Release/nyquist/rawwaves/mand1.raw deleted file mode 100644 index bc04a0583599515565866453df6016d6d1c27694..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand1.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand10.raw b/Release/nyquist/rawwaves/mand10.raw deleted file mode 100644 index 4b35376aeacdfc50cbef89ea76259403ac542b69..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand10.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand11.raw b/Release/nyquist/rawwaves/mand11.raw deleted file mode 100644 index 94889be6f0d6dfeaa63449e06a39df1356508dac..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand11.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand12.raw b/Release/nyquist/rawwaves/mand12.raw deleted file mode 100644 index a128642bf6de4fdac74a1fa03bf32bc6003bb685..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand12.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand2.raw b/Release/nyquist/rawwaves/mand2.raw deleted file mode 100644 index 62080081d289b7dfa042b787190b99b0399fe81e..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand2.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand3.raw b/Release/nyquist/rawwaves/mand3.raw deleted file mode 100644 index 8857f862298c1fd4516047ce8f0daa096a08ebf4..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand3.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand4.raw b/Release/nyquist/rawwaves/mand4.raw deleted file mode 100644 index 6058eb10081839bb84810134acd9252f873b8bfc..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand4.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand5.raw b/Release/nyquist/rawwaves/mand5.raw deleted file mode 100644 index 9b308a860b4966a4bfa6148f01bdfa70fdc99495..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand5.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand6.raw b/Release/nyquist/rawwaves/mand6.raw deleted file mode 100644 index 05f083d8912b7622d17e058ced75e9c06d65f25d..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand6.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand7.raw b/Release/nyquist/rawwaves/mand7.raw deleted file mode 100644 index 64941e9f98421aa1b2b353abbfa5a25cd9942295..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand7.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand8.raw b/Release/nyquist/rawwaves/mand8.raw deleted file mode 100644 index 52027bf695e8053eb0511e36ac16a38a500d7973..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand8.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mand9.raw b/Release/nyquist/rawwaves/mand9.raw deleted file mode 100644 index 9e88a0c91317db8a3f2ba7969fe7aa0e0b2e029f..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mand9.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/mandpluk.raw b/Release/nyquist/rawwaves/mandpluk.raw deleted file mode 100644 index 162a0da9e1d448ee915c7b4ba441c8da46b6298a..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/mandpluk.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/marmstk1.raw b/Release/nyquist/rawwaves/marmstk1.raw deleted file mode 100644 index 185b4452613d748bf21d19614a54cd139e7d4b40..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/marmstk1.raw and /dev/null differ diff --git a/Release/nyquist/rawwaves/sinewave.raw b/Release/nyquist/rawwaves/sinewave.raw deleted file mode 100644 index a5cb34991bc7de10b0f342dc72b9f295a4eff628..0000000000000000000000000000000000000000 Binary files a/Release/nyquist/rawwaves/sinewave.raw and /dev/null differ diff --git a/Release/nyquist/sal-parse.lsp b/Release/nyquist/sal-parse.lsp deleted file mode 100644 index 461ce057dc0792a80c0ac4efc80f1deaae3d7993..0000000000000000000000000000000000000000 --- a/Release/nyquist/sal-parse.lsp +++ /dev/null @@ -1,1899 +0,0 @@ -;; SAL parser -- replaces original pattern-directed parser with -;; a recursive descent one -;; -;; Parse functions either parse correctly and return -;; compiled code as a lisp expression (which could be nil) -;; or else they call parse-error, which does not return -;; (instead, parse-error forces a return from parse) -;; In the original SAL parser, triples were returned -;; including the remainder if any of the tokens to be -;; parsed. In this parser, tokens are on the list -;; *sal-tokens*, and whatever remains on the list is -;; the list of unparsed tokens. - -;; scanning delimiters. - -(setfn nreverse reverse) - -(defconstant +quote+ #\") ; "..." string -(defconstant +kwote+ #\') ; '...' kwoted expr -(defconstant +comma+ #\,) ; positional arg delimiter -(defconstant +pound+ #\#) ; for bools etc -(defconstant +semic+ #\;) ; comment char -(defconstant +lbrace+ #\{) ; {} list notation -(defconstant +rbrace+ #\}) -(defconstant +lbrack+ #\[) ; unused for now -(defconstant +rbrack+ #\]) -(defconstant +lparen+ #\() ; () expr and arg grouping -(defconstant +rparen+ #\)) - -;; these are defined so that SAL programs can name these symbols -;; note that quote(>) doesn't work, so you need quote(symbol:greater) - -(setf symbol:greater '>) -(setf symbol:less '<) -(setf symbol:greater-equal '>=) -(setf symbol:less-equal '<=) -(setf symbol:equal '=) -(setf symbol:not '!) -(setf symbol:not-equal '/=) - - -(defparameter +whites+ (list #\space #\tab #\newline (code-char 13))) - -(defparameter +kwstyle+ (list :suffix #\:)) ; let's try dylan - -(defparameter +operators+ - ;; each op is: (<token-class> <sal-name> <lisp-form>) - '((:+ "+" sal-plus) - (:- "-" diff) - (:* "*" mult) - (:/ "/" /) - (:% "%" rem) - (:^ "^" expt) - (:= "=" sal-equal) ; equality and assignment - (:!= "!=" not-sal-equal) - (:< "<" <) - (:> ">" >) - (:<= "<=" <=) ; leq and assignment minimization - (:>= ">=" >=) ; geq and assignment maximization - (:~= "~=" sal-about-equal) ; general equality - (:+= "+=" +=) ; assignment increment-and-store - (:-= "-=" -=) ; assignment increment-and-store - (:*= "*=" *=) ; assignment multiply-and-store - (:/= "/=" /=) ; assignment multiply-and-store - (:&= "&=" &=) ; assignment list collecting - (:@= "@=" @=) ; assignment list prepending - (:^= "^=" ^=) ; assignment list appending - (:! "!" not) - (:& "&" and) - (:\| "|" or) - (:~ "~" sal-stretch) - (:~~ "~~" sal-stretch-abs) - (:@ "@" sal-at) - (:@@ "@@" sal-at-abs) - )) - -(setf *sal-local-variables* nil) ;; used to avoid warning about variable - ;; names when the variable has been declared as a local - -(defparameter *sal-operators* - '(:+ :- :* :/ :% :^ := :!= :< :> :<= :>= :~= :+= :*= :&= :@= :^= :! :& :\| - :~ :~~ :@ :@@)) - -(defparameter +delimiters+ - '((:lp #\() - (:rp #\)) - (:lc #\{) ; left curly - (:rc #\}) - (:lb #\[) - (:rb #\]) - (:co #\,) - (:kw #\') ; kwote - (nil #\") ; not token - ; (nil #\#) - (nil #\;) - )) - -(setf *reserved-words* '((::+ ":+") (::- ":-") (::* ":*") (::/ ":/") (::= ":=") - (::!= ":!=") (::< ":<") (::> ":>") (::<= ":<=") - (::>= ":>=") (::~= ":~=") (::! ":!") (::& ":&") - (::\| ":|") (:IF "if") (:THEN "then") (:ELSE "else") - (:WHEN "when") (:UNLESS "unless") (:SET "set") - (:= "=") (:+= "+=") (:*= "*=") (:&= "&=") (:@= "@=") - (:^= "^=") (:<= "<=") (:>= ">=") (:PRINT "print") - (:LOOP "loop") (:SEQV "seqv") (:SEQREPV "seqrepv") - (:RUN "run") (:REPEAT "repeat") (:FOR "for") - (:FROM "from") (:IN "in") (:BELOW "below") (:TO "to") - (:ABOVE "above") (:DOWNTO "downto") (:BY "by") - (:OVER "over") (:WHILE "while") (:UNTIL "until") - (:FINALLY "finally") (:RETURN "return") - (:WAIT "wait") (:BEGIN "begin") (:WITH "with") - (:END "end") (:VARIABLE "variable") - (:FUNCTION "function") - ; not in nyquist: (:PROCESS "process") - (:CHDIR "chdir") - (:DEFINE "define") (:LOAD "load") - (:PLAY "play") (:PLOT "plot") - (:EXEC "exec") (:exit "exit") (:DISPLAY "display") - (:~ "~") (:~~ "~~") (:@ ":@") (:@@ ":@@"))) - - -(setf *sal-fn-name* nil) - -(defun make-sal-error (&key type text (line nil) start) - ; (error 'make-sal-error-was-called-break) - (list 'sal-error type text line start)) -(setfn sal-error-type cadr) -(setfn sal-error-text caddr) -(setfn sal-error-line cadddr) -(defun sal-error-start (x) (cadddr (cdr x))) -(defun is-sal-error (x) (and x (eq (car x) 'sal-error))) -(defun sal-tokens-error-start (start) - (cond (start - start) - (*sal-tokens* - (token-start (car *sal-tokens*))) - (t - (length *sal-input-text*)))) - - -(defmacro errexit (message &optional start) - `(parse-error (make-sal-error :type "parse" - :line *sal-input-text* :text ,message - :start ,(sal-tokens-error-start start)))) - -(defmacro sal-warning (message &optional start) - `(pperror (make-sal-error :type "parse" :line *sal-input-text* - :text ,message - :start ,(sal-tokens-error-start start)) - "warning")) - -(setf *pos-to-line-source* nil) -(setf *pos-to-line-pos* nil) -(setf *pos-to-line-line* nil) - -(defun pos-to-line (pos source) - ;; this is really inefficient to search every line from - ;; the beginning, so cache results and search forward - ;; from there if possible - (let ((i 0) (line-no 1)) ;; assume no cache - ;; see if we can use the cache - (cond ((and (eq source *pos-to-line-source*) - *pos-to-line-pos* *pos-to-line-line* - (>= pos *pos-to-line-pos*)) - (setf i *pos-to-line-pos*) - (setf line-no *pos-to-line-line*))) - ;; count newlines up to pos - (while (< i pos) - (if (char= (char source i) #\newline) - (incf line-no)) - (setf i (1+ i))) - ;; save results in cache - (setf *pos-to-line-source* source - *pos-to-line-pos* pos - *pos-to-line-line* line-no) - ;; return the line number at pos in source - line-no)) - - -;; makes a string of n spaces, empty string if n <= 0 -(defun make-spaces (n) - (cond ((> n 16) - (let* ((half (/ n 2)) - (s (make-spaces half))) - (strcat s s (make-spaces (- n half half))))) - (t - (subseq " " 0 (max n 0))))) - - -(defun pperror (x &optional (msg-type "error")) - (let* ((source (sal-error-line x)) - (llen (length source)) - line-no - beg end) - ; (display "pperror" x (strcat "|" (sal-error-line x) "|")) - ;; isolate line containing error - (setf beg (sal-error-start x)) - (setf beg (min beg (1- llen))) - (do ((i beg (- i 1)) - (n nil)) ; n gets set when we find a newline - ((or (< i 0) n) - (setq beg (or n 0))) - (if (char= (char source i) #\newline) - (setq n (+ i 1)))) - (do ((i (sal-error-start x) (+ i 1)) - (n nil)) - ((or (>= i llen) n) - (setq end (or n llen))) - (if (char= (char source i) #\newline) - (setq n i))) - (setf line-no (pos-to-line beg source)) - ; (display "pperror" beg end (sal-error-start x)) - - ;; print the error. include the specific line of input containing - ;; the error as well as a line below it marking the error position - ;; with an arrow: ^ - (let* ((pos (- (sal-error-start x) beg)) - (line (if (and (= beg 0) (= end llen)) - source - (subseq source beg end))) - (mark (make-spaces pos))) - (format t "~%>>> ~A ~A: ~A.~%>>> in ~A, line ~A, col ~A.~%~%~A~%~A^~%" - (sal-error-type x) msg-type (sal-error-text x) - *sal-input-file-name* line-no (1+ pos) - line mark) -; (format t "~%>>> ~A error in \"~A\", line ~A, col ~A: ~A.~%~%~A~%~A^~%" -; (sal-error-type x) *sal-input-file-name* line-no pos -; (sal-error-text x) line mark) - x))) - - -;;; -;;; the lexer. right now it assumes input string is complete and ready -;;; to be processed as a valid expression. -;;; - -(defun advance-white (str white start end) - ;; skip "white" chars, where white can be a char, list of chars - ;; or predicate test - (do ((i start ) - (p nil)) - ((or p (if (< start end) - (not (< -1 i end)) - (not (> i end -1)))) - (or p end)) - (cond ((consp white) - (unless (member (char str i) white :test #'char=) - (setq p i))) - ((characterp white) - (unless (char= (char str i) white) - (setq p i))) - ((functionp white) - (unless (funcall white (char str i)) - (setq p i)))) - (if (< start end) - (incf i) - (decf i)))) - - -(defun search-delim (str delim start end) - ;; find position of "delim" chars, where delim can be - ;; a char, list of chars or predicate test - (do ((i start (+ i 1)) - (p nil)) - ((or (not (< i end)) p) - (or p end)) - (cond ((consp delim) - (if (member (char str i) delim :test #'char=) - (setq p i))) - ((characterp delim) - (if (char= (char str i) delim) - (setq p i))) - ((functionp delim) - (if (funcall delim (char str i)) - (setq p i)))))) - - -;; UNBALANCED-INPUT AND TOKENIZE HAVE BEEN REWRITTEN, SEE BELOW. THIS ONE IS -;; OLD AND JUST KEPT HERE FOR REFERENCE -#| -(defun unbalanced-input (errf line toks par bra brk kwo) - ;; search input for the starting position of some unbalanced - ;; delimiter, toks is reversed list of tokens with something - ;; unbalanced - (let (char text targ othr levl pos) - (cond ((> par 0) (setq char #\( targ ':lp othr ':rp levl par)) - ((< par 0) (setq char #\) targ ':rp othr ':lp levl 0)) - ((> bra 0) (setq char #\{ targ ':lc othr ':rc levl bra)) - ((< bra 0) (setq char #\} targ ':rc othr ':lc levl 0)) - ((> brk 0) (setq char #\[ targ ':ls othr ':rs levl brk)) - ((< brk 0) (setq char #\] targ ':rs othr ':ls levl 0)) - ((> kwo 0) (setq char #\' targ ':kw othr ':kw levl kwo))) - (setq text (format nil "Unmatched '~A'" char)) - ;; search for start of error in token list - (do ((n levl) - (tail toks (cdr tail))) - ((or (null tail) pos) - (or pos (error (format nil "Shouldn't! can't find op ~A in ~A." - targ (reverse toks))))) - (if (eql (token-type (car tail)) targ) - (if (= n levl) - (setq pos (token-start (car tail))) - (decf n)) - (if (eql (token-type (car tail)) othr) - (incf n)))) - (errexit text pos))) - -;; REMINDER: THIS IS PART OF A BIG BLOCK COMMENT -(defun tokenize (str reserved error-fn) - ;&key (start 0) (end (length str)) - ; (white-space +whites+) (delimiters +delimiters+) - ; (operators +operators+) (null-ok t) - ; (keyword-style +kwstyle+) (reserved nil) - ; (error-fn nil) - ; &allow-other-keys) - ;; return zero or more tokens or a sal-error - (let ((toks (list t)) - (start 0) - (end (length str)) - (all-delimiters +whites+) - (errf (or error-fn - (lambda (x) (pperror x) (return-from tokenize x))))) - (dolist (x +delimiters+) - (push (cadr x) all-delimiters)) - (do ((beg start) - (pos nil) - (all all-delimiters) - (par 0) - (bra 0) - (brk 0) - (kwo 0) - (tok nil) - (tail toks)) - ((not (< beg end)) - ;; since input is complete check parens levels. - (if (= 0 par bra brk kwo) - (if (null (cdr toks)) - (list) - (cdr toks)) - (unbalanced-input errf str (reverse (cdr toks)) - par bra brk kwo))) - (setq beg (advance-white str +whites+ beg end)) - (setf tok - (read-delimited str :start beg :end end - :white +whites+ :delimit all - :skip-initial-white nil :errorf errf)) - ;; multiple values are returned, so split them here: - (setf pos (second tok)) ; pos is the end of the token (!) - (setf tok (first tok)) - - ;; tok now string, char (delimiter), :eof or token since input - ;; is complete keep track of balancing delims - (cond ((eql tok +lbrace+) (incf bra)) - ((eql tok +rbrace+) (decf bra)) - ((eql tok +lparen+) (incf par)) - ((eql tok +rparen+) (decf par)) - ((eql tok +lbrack+) (incf brk)) - ((eql tok +rbrack+) (decf brk)) - ((eql tok +kwote+) (setq kwo (mod (+ kwo 1) 2)))) - (cond ((eql tok ':eof) - (setq beg end)) - - (t - ;; may have to skip over comments to reach token, so - ;; token beginning is computed by backing up from current - ;; position (returned by read-delimited) by string length - (setf beg (if (stringp tok) - (- pos (length tok)) - (1- pos))) - (setq tok (classify-token tok beg str errf - +delimiters+ +operators+ - +kwstyle+ reserved)) - ;(display "classify-token-result" tok) - (setf (cdr tail) (list tok )) - (setf tail (cdr tail)) - (setq beg pos)))))) -|# - - -;; old tokenize (above) counted delimiters to check for balance, -;; but that does not catch constructions like ({)}. I think -;; we could just leave this up to the parser, but this rewrite -;; uses a stack to check balanced parens, braces, quotes, etc. -;; The checking establishes at least some minimal global properties -;; of the input before evaluating anything, which might be good -;; even though it's doing some extra work. In fact, using a -;; stack rather than counts is doing even more work, but the -;; problem with counters is that some very misleading or just -;; plain wrong error messages got generated. -;; -;; these five delimiter- functions do checks on balanced parens, -;; braces, and brackets, leaving delimiter-mismatch set to bad -;; token if there is a mismatch -(defun delimiter-init () - (setf delimiter-stack nil) - (setf delimiter-mismatch nil)) -(defun delimiter-match (tok what) - (cond ((eql (token-string (first delimiter-stack)) what) - (pop delimiter-stack)) - ((null delimiter-mismatch) - ;(display "delimiter-mismatch" tok) - (setf delimiter-mismatch tok)))) -(defun delimiter-check (tok) - (let ((c (token-string tok))) - (cond ((member c '(#\( #\{ #\[)) - (push tok delimiter-stack)) - ((eql c +rbrace+) - (delimiter-match tok +lbrace+)) - ((eql c +rparen+) - (delimiter-match tok +lparen+)) - ((eql c +rbrack+) - (delimiter-match tok +lbrack+))))) -(defun delimiter-error (tok) - (errexit (format nil "Unmatched '~A'" (token-string tok)) - (token-start tok))) -(defun delimiter-finish () - (if delimiter-mismatch - (delimiter-error delimiter-mismatch)) - (if delimiter-stack - (delimiter-error (car delimiter-stack)))) -(defun tokenize (str reserved error-fn) - ;; return zero or more tokens or a sal-error - (let ((toks (list t)) - (start 0) - (end (length str)) - (all-delimiters +whites+) - (errf (or error-fn - (lambda (x) (pperror x) (return-from tokenize x))))) - (dolist (x +delimiters+) - (push (cadr x) all-delimiters)) - (delimiter-init) - (do ((beg start) - (pos nil) - (all all-delimiters) - (tok nil) - (tail toks)) - ((not (< beg end)) - ;; since input is complete check parens levels. - (delimiter-finish) - (if (null (cdr toks)) nil (cdr toks))) - (setq beg (advance-white str +whites+ beg end)) - (setf tok - (read-delimited str :start beg :end end - :white +whites+ :delimit all - :skip-initial-white nil :errorf errf)) - ;; multiple values are returned, so split them here: - (setf pos (second tok)) ; pos is the end of the token (!) - (setf tok (first tok)) - - (cond ((eql tok ':eof) - (setq beg end)) - (t - ;; may have to skip over comments to reach token, so - ;; token beginning is computed by backing up from current - ;; position (returned by read-delimited) by string length - (setf beg (if (stringp tok) - (- pos (length tok)) - (1- pos))) - (setq tok (classify-token tok beg str errf - +delimiters+ +operators+ - +kwstyle+ reserved)) - (delimiter-check tok) - ;(display "classify-token-result" tok) - (setf (cdr tail) (list tok )) - (setf tail (cdr tail)) - (setq beg pos)))))) - - -(defun read-delimited (input &key (start 0) end (null-ok t) - (delimit +delims+) ; includes whites... - (white +whites+) - (skip-initial-white t) - (errorf #'pperror)) - ;; read a substring from input, optionally skipping any white chars - ;; first. reading a comment delim equals end-of-line, input delim - ;; reads whole input, pound reads next token. call errf if error - ;(FORMAT T "~%READ-DELIMITED: ~S :START ~S :END ~S" input start end) - (let ((len (or end (length input)))) - (while t ;; loop over comment lines - (when skip-initial-white - (setq start (advance-white input white start len))) - (if (< start len) - (let ((char (char input start))) - (setq end (search-delim input delimit start len)) - (if (equal start end) ; have a delimiter - (cond ((char= char +semic+) - ;; comment skips to next line and try again... - (while (and (< start len) - (char/= (char input start) #\newline)) - (incf start)) - (cond ((< start len) ;; advance past comment and iterate - (incf start) - (setf skip-initial-white t)) - (null-ok - (return (list ':eof end))) - (t - (errexit "Unexpected end of input")))) -; ((char= char +pound+) -; ;; read # dispatch -; (read-hash input delimit start len errorf)) - ((char= char +quote+) - ;; input delim reads whole input - (return (sal:read-string input delimit start len errorf))) - ((char= char +kwote+) - (errexit "Illegal delimiter" start)) - (t ;; all other delimiters are tokens in and of themselves - (return (list char (+ start 1))))) - ; else part of (equal start end), so we have token before delimiter - (return (list (subseq input start end) end)))) - ; else part of (< start len)... - (if null-ok - (return (list ':eof end)) - (errexit "Unexpected end of input" start)))))) - - -(defparameter hash-readers - '(( #\t sal:read-bool) - ( #\f sal:read-bool) - ( #\? read-iftok) - )) - - -(defun read-hash (str delims pos len errf) - (let ((e (+ pos 1))) - (if (< e len) - (let ((a (assoc (char str e) hash-readers))) - (if (not a) - (errexit "Illegal # character" e) - (funcall (cadr a) str delims e len errf))) - (errexit "Missing # character" pos)))) - - -(defun read-iftok (str delims pos len errf) - str delims len errf - (list (make-token :type ':? :string "#?" :lisp 'if - :start (- pos 1)) - (+ pos 1))) - -; (sal:read-string str start len) - -(defun sal:read-bool (str delims pos len errf) - delims len errf - (let ((end (search-delim str delims pos len))) - (unless (= end (+ pos 1)) - (errexit "Illegal # expression" (- pos 1))) - (list (let ((t? (char= (char str pos) #\t) )) - (make-token :type ':bool - :string (if t? "#t" "#f") - :lisp t? - :start (- pos 1))) - (+ pos 1)))) - - -(defun sal:read-string (str delims pos len errf) - (let* ((i (1+ pos)) ; i is index into string; start after open quote - c c2; c is the character at str[i] - (string (make-string-output-stream))) - ;; read string, processing escaped characters - ;; write the chars to string until end quote is found - ;; then retrieve the string. quotes are not included in result token - - ;; in the loop, i is the next character location to examine - (while (and (< i len) - (not (char= (setf c (char str i)) +quote+))) - (if (char= c #\\) ;; escape character, does another character follow this? - (cond ((< (1+ i) len) - (incf i) ;; yes, set i so we'll get the escaped char - (setf c2 (char str i)) - (setf c (assoc c2 `((#\n . #\newline) (#\t . #\tab) - (#\r . ,(char "\r" 0)) - (#\f . ,(char "\f" 0))))) - (setf c (if c (cdr c) c2))) ;; use c2 if c wasn't listed - (t ;; no, we've hit the end of input too early - (errexit "Unmatched \"" i)))) - ;; we're good to take this character and move on to the next one - (write-char c string) - (incf i)) - ;; done with loop, so either we're out of string or we found end quote - (if (>= i len) (errexit "Unmatched \"" i)) - ;; must have found the quote - (setf string (get-output-stream-string string)) - (list (make-token :type :string :start pos :string string :lisp string) - (1+ i)))) - -;;; -;;; tokens -;;; - -(defun make-token (&key (type nil) (string "") start (info nil) lisp) - (list :token type string start info lisp)) -(setfn token-type cadr) -(setfn token-string caddr) -(defun token-start (x) (cadddr x)) -(defun token-info (token) (car (cddddr token))) -(defun token-lisp (token) (cadr (cddddr token))) -(defmacro set-token-type (tok val) `(setf (car (cdr ,tok)) ,val)) -(defmacro set-token-lisp (tok val) `(setf (car (cdr (cddddr ,tok))) ,val)) -(defun tokenp (tok) (and (consp tok) (eq (car tok) :token))) - -(defun token=? (tok op) - (if (tokenp tok) - (equal (token-type tok) op) - (eql tok op))) - -(defmethod token-print (obj stream) - (let ((*print-case* ':downcase)) - (format stream "#<~s ~s>" - (token-type obj) - (token-string obj)))) - -(defun parse-token () - (prog1 (car *sal-tokens*) - (setf *sal-tokens* (cdr *sal-tokens*)))) - -;;; -;;; token classification. types not disjoint! -;;; - -(defun classify-token (str pos input errf delims ops kstyle res) - (let ((tok nil)) - (cond ((characterp str) - ;; normalize char delimiter tokens - (setq tok (delimiter-token? str pos input errf delims))) - ((stringp str) - (setq tok (or (number-token? str pos input errf) - (operator-token? str pos input errf ops) - (keyword-token? str pos input errf kstyle) - (class-token? str pos input errf res) - (reserved-token? str pos input errf res) - (symbol-token? str pos input errf) - )) - (unless tok - (errexit "Not an expression or symbol" pos))) - (t (setq tok str))) - tok)) - - -(defun delimiter-token? (str pos input errf delims) - (let ((typ (member str delims :test (lambda (a b) (char= a (cadr b)))))) - ;; member returns remainder of the list - ;(display "delimiter-token?" str delims typ) - (if (and typ (car typ) (caar typ)) - (make-token :type (caar typ) :string str - :start pos) - (+ (break) (errexit "Shouldn't: non-token delimiter" pos))))) - - -(defun string-to-number (s) - (read (make-string-input-stream s))) - - -(defun number-token? (str pos input errf) - errf input - (do ((i 0 (+ i 1)) - (len (length str)) - (c nil) - (dot 0) - (typ ':int) - (sig 0) - (sla 0) - (dig 0) - (non nil)) - ((or (not (< i len)) non) - (if non nil - (if (> dig 0) - (make-token :type typ :string str - :start pos :lisp (string-to-number str)) - nil))) - (setq c (char str i)) - (cond ((member c '(#\+ #\-)) - (if (> i 0) (setq non t) - (incf sig))) - ((char= c #\.) - (if (> dot 0) (setq non t) - (if (> sla 0) (setq non t) - (incf dot)))) -; xlisp does not have ratios -; ((char= c #\/) -; (setq typ ':ratio) -; (if (> sla 0) (setq non t) -; (if (= dig 0) (setq non t) -; (if (> dot 0) (setq non t) -; (if (= i (1- len)) (setq non t) -; (incf sla)))))) - ((digit-char-p c) - (incf dig) - (if (> dot 0) (setq typ ':float))) - (t (setq non t))))) - -#|| -(number-token? "" 0 "" #'pperror) -(number-token? " " 0 "" #'pperror) -(number-token? "a" 0 "" #'pperror) -(number-token? "1" 0 "" #'pperror) -(number-token? "+" 0 "" #'pperror) -(number-token? "-1/2" 0 "" #'pperror) -(number-token? "1." 0 "" #'pperror) -(number-token? "1.." 0 "" #'pperror) -(number-token? ".1." 0 "" #'pperror) -(number-token? ".1" 0 "" #'pperror) -(number-token? "-0.1" 0 "" #'pperror) -(number-token? "1/2" 0 "" #'pperror) -(number-token? "1//2" 0 "" #'pperror) -(number-token? "/12" 0 "" #'pperror) -(number-token? "12/" 0 "" #'pperror) -(number-token? "12/1" 0 "" #'pperror) -(number-token? "12./1" 0 "" #'pperror) -(number-token? "12/.1" 0 "" #'pperror) -||# - -(defun operator-token? (str pos input errf ops) - ;; tok can be string or char - (let ((typ (member str ops :test (lambda (a b) (equal a (cadr b)))))) - (cond (typ - (setf typ (car typ)) ;; member returns remainder of list - (make-token :type (car typ) :string str - :start pos :lisp (or (third typ) - (read-from-string str))))))) - -(defun str-to-keyword (str) - (intern (strcat ":" (string-upcase str)))) - - -(defun keyword-token? (tok pos input errf style) - (let* ((tlen (length tok)) - (keys (cdr style)) - (klen (length keys))) - (cond ((not (< klen tlen)) nil) - ((eql (car style) ':prefix) - (do ((i 0 (+ i 1)) - (x nil)) - ((or (not (< i klen)) x) - (if (not x) - (let ((sym (symbol-token? (subseq tok i) - pos input errf ))) - (cond (sym - (set-token-type sym ':key) - (set-token-lisp sym - (str-to-keyword (token-string sym))) - sym))) - nil)) - (unless (char= (char tok i) (nth i keys)) - (setq x t)))) - ((eql (car style) ':suffix) - (do ((j (- tlen klen) (+ j 1)) - (i 0 (+ i 1)) - (x nil)) - ((or (not (< i klen)) x) - (if (not x) - (let ((sym (symbol-token? (subseq tok 0 (- tlen klen)) - pos input errf ))) - (cond (sym - (set-token-type sym ':key) - (set-token-lisp sym - (str-to-keyword (token-string sym))) - sym))) - nil)) - (unless (char= (char tok j) (nth i keys)) - (setq x t))))))) - - -(setfn alpha-char-p both-case-p) - - -(defun class-token? (str pos input errf res) - res - (let ((a (char str 0))) - (if (char= a #\<) - (let* ((l (length str)) - (b (char str (- l 1)))) - (if (char= b #\>) - (let ((tok (symbol-token? (subseq str 1 (- l 1)) - pos input errf))) - ;; class token has <> removed! - (if tok (progn (set-token-type tok ':class) - tok) - (errexit "Not a class identifier" pos))) - (errexit "Not a class identifer" pos))) - nil))) - -; (keyword-token? ":asd" '(:prefix #\:)) -; (keyword-token? "asd" KSTYLE) -; (keyword-token? "asd:" KSTYLE) -; (keyword-token? "123:" KSTYLE) -; (keyword-token? ":foo" '(:prefix #\:)) -; (keyword-token? "foo=" '(:suffix #\=)) -; (keyword-token? "--foo" '(:prefix #\- #\-)) -; (keyword-token? ":123" '(:suffix #\:)) -; (keyword-token? "--asd" '(:prefix #\-)) ; ok since -asd is legal symbol - - -;; determine if str is a reserved word using reserved as the list of -;; reserved words, of the form ((id string) (id string) ...) where -;; id identifies the token, e.g. :to and string is the token, e.g. "to" -;; -(defun reserved-token? (str pos input errf reserved) - errf input - (let ((typ (member str reserved :test - (lambda (a b) (string-equal a (cadr b)))))) - (if typ - (make-token :type (caar typ) :string str - :start pos) - nil))) - - -(defun sal-string-to-symbol (str) - (let ((sym (intern (string-upcase str))) - sal-sym) - (cond ((and sym ;; (it might be "nil") - (setf sal-sym (get sym :sal-name))) - sal-sym) - (t sym)))) - - -(putprop 'simrep 'sal-simrep :sal-name) -(putprop 'seqrep 'sal-seqrep :sal-name) - -(defun contains-op-char (s) - ;; assume most identifiers are very short, so we search - ;; over identifier letters, not over operator characters - ;; Minus (-) is so common, we don't complain about it. - (dotimes (i (length s)) - (if (string-search (subseq s i (1+ i)) "*/+=<>!%^&|") - (return t)))) - -(defun test-for-suspicious-symbol (token) - ;; assume token is of type :id - (let ((sym (token-lisp token)) - (str (token-string token)) - (pos (token-start token))) - (cond ((and sym ; nil is not suspicious, but it's not "boundp" - (not (fboundp sym)) ; existing functions not suspicious - (not (boundp sym)) ; existing globals not suspicious - (not (member sym *sal-local-variables*)) - (not (eq sym '->)) ; used by make-markov, so let it pass - (contains-op-char str)) ; suspicious if embedded operators - (sal-warning - (strcat "Identifier contains operator character(s).\n" - " Perhaps you omitted spaces around an operator") - pos))))) - - -(defun symbol-token? (str pos input errf) - ;; if a potential symbol is preceded by #, drop the # - (if (and (> (length str) 1) - (char= (char str 0) #\#)) - ;; there are a couple of special cases: SAL defines #f and #? - (cond ((equal str "#f") - (return-from symbol-token? - (make-token :type ':id :string str :start pos :lisp nil))) - ((equal str "#?") - (return-from symbol-token? - (make-token :type ':id :string str :start pos :lisp 'if))) - (t - (setf str (subseq str 1))))) - ;; let's insist on at least one letter for sanity's sake - ;; exception: allow '-> because it is used in markov pattern specs - (do ((i 0 (+ i 1)) ; i is index into string - (bad "Not an expression or symbol") - (chr nil) - (ltr 0) ; ltr is count of alphabetic letters in string - (dot nil) ; dot is index of "." - (pkg nil) ; pkg is index if package name "xxx:" found - (len (length str)) - (err nil)) - ;; loop ends when i is at end of string or when err is set - ((or (not (< i len)) err) - (if (or (> ltr 0) ; must be at least one letter, or - (equal str "->")) ; symbol can be "->" - (let ((info ()) sym) - (if pkg (push (cons ':pkg pkg) info)) - (if dot (push (cons ':slot dot) info)) - ;(display "in symbol-token?" str) - (setf sym (sal-string-to-symbol str)) - (make-token :type ':id :string str - :info info :start pos - :lisp sym)) - nil)) - (setq chr (char str i)) - (cond ((alpha-char-p chr) (incf ltr)) -; need to allow arbitrary lisp symbols -; ((member chr '(#\* #\+)) ;; special variable names can start/end -; (if (< 0 i (- len 2)) ;; with + or * -; (errexit bad pos))) - ((char= chr #\/) ;; embedded / is not allowed - (errexit bad pos)) - ;((char= chr #\-) ;; hyphens are allowed anywhere in symbol - ; (if (= ltr 0) - ; (errexit errf input bad pos ) - ; (setq ltr 0) - ; )) - ((char= chr #\$) (incf ltr)) ;; "$" is treated as a letter - ((char= chr #\:) - ; allowable forms are :foo, foo:bar, :foo:bar - (if (> i 0) ;; lisp keyword symbols ok - (cond ((= ltr 0) - (errexit bad pos)) - ((not pkg) - (setq pkg i)) - (t (errexit errf input - (format nil "Too many colons in ~s" str) - pos)))) - (setq ltr 0)) - ((char= chr #\.) - (if (or dot (= i 0) (= i (- len 1))) - (errexit bad pos) - (progn (setq dot i) (setq ltr 0))))))) - - -; (let ((i "foo")) (symbol-token? i 0 i #'pperror)) -; (let ((i "foo..bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i ".bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i "bar.")) (symbol-token? i 0 i #'pperror)) -; (let ((i "1...")) (symbol-token? i 0 i #'pperror)) -; (let ((i "a1..." )) (symbol-token? i 0 i #'pperror)) -; (let ((i "a{b")) (symbol-token? i 0 i #'pperror)) -; (let ((i "foo-bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i "123-a")) (symbol-token? i 0 i #'pperror)) -; (let ((i "1a23-a")) (symbol-token? i 0 i #'pperror)) -; (let ((i "*foo*")) (symbol-token? i 0 i #'pperror)) -; (let ((i "+foo+")) (symbol-token? i 0 i #'pperror)) -; (let ((i "foo+bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i "foo/bar")) (symbol-token? i 0 i #'pperror)) - -; (let ((i ":bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i "::bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i "foo:bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i "cl-user:bar")) (symbol-token? i 0 i #'pperror)) -; (let ((i "cl-user::bar")) (symbol-token? i 0 i #'pperror)) -; (tokenize "aaa + bbb \"asdasdd\" aaa(1,2,3)") -; (tokenize "aaa+bbb \"asdasdd\" aaa(1,2,3)") - - -(setf *in-sal-parser* nil) - -;; line number info for debugging -(setf *sal-line-number-info* t) -(setf *sal-line* 0) - -(defun add-line-info-to-expression (expr token) - (let (line-no) - (cond ((and token ;; null token means do not change expr - *sal-line-number-info* ;; is this feature enabled? - (stringp *sal-input-text*)) - ;; first, get line number - (setf line-no (pos-to-line (token-start token) *sal-input-text*)) - `(prog2 (setf *sal-line* ,line-no) ,expr)) - (t expr)))) - -;; single statement is handled just like an expression -(setfn add-line-info-to-stmt add-line-info-to-expression) - -;; list of statements is simple to handle: prepend SETF -(defun add-line-info-to-stmts (stmts token) - (let (line-no) - (cond ((and *sal-line-number-info* ;; is this feature enabled? - (stringp *sal-input-text*)) - (setf line-no (pos-to-line (token-start token) *sal-input-text*)) - (cons `(setf *sal-line* ,line-no) stmts)) - (t stmts)))) - - -;; PARSE-ERROR -- print error message, return from top-level -;; -(defun parse-error (e) - (unless (sal-error-line e) - (setf (sal-error-line e) *sal-input*)) - (pperror e) - (return-from sal-parse (values nil e *sal-tokens*))) - - -;; SAL-PARSE -- parse string or token input, translate to Lisp -;; -;; If input is text, *sal-input-text* is set to the text and -;; read later (maybe) by ERREXIT. -;; If input is a token list, it is assumed these are leftovers -;; from tokenized text, so *sal-input-text* is already valid. -;; *Therefore*, do not call sal-parse with tokens unless -;; *sal-input-text* is set to the corresponding text. -;; -(defun sal-parse (grammar pat input multiple-statements file) - (progv '(*sal-input-file-name*) (list file) - (let (rslt expr rest) - ; ignore grammar and pat (just there for compatibility) - ; parse input and return lisp expression - (cond ((stringp input) - (setf *sal-input-text* input) - (setq input (tokenize input *reserved-words* #'parse-error)))) - (setf *sal-input* input) ;; all input - (setf *sal-tokens* input) ;; current input - (cond ((null input) - (values t nil nil)) ; e.g. comments compile to nil - (t - (setf rslt (or (maybe-parse-command) - (maybe-parse-block) - (maybe-parse-conditional) - (maybe-parse-assignment) - (maybe-parse-loop) - (maybe-parse-exec) - (maybe-parse-exit) - (errexit "Syntax error"))) - ;; note: there is a return-from parse in parse-error that - ;; returns (values nil error <unparsed-tokens>) - (cond ((and *sal-tokens* (not multiple-statements)) - (errexit "leftover tokens"))) - ;((null rslt) - ; (errexit "nothing to compile"))) - (values t rslt *sal-tokens*)))))) - - -;; TOKEN-IS -- test if the type of next token matches expected type(s) -;; -;; type can be a list of possibilities or just a symbol -;; Usually, suspicious-id-warn is true by default, and any symbol -;; with embedded operator symbols, e.g. x+y results in a warning -;; that this is an odd variable name. But if the symbol is declared -;; as a local, a parameter, a function name, or a global variable, -;; then the warning is suppressed. -;; -(defun token-is (type &optional (suspicious-id-warn t)) - (let ((token-type - (if *sal-tokens* (token-type (car *sal-tokens*)) nil)) - rslt) - ; input can be list of possible types or just a type: - (setf rslt (or (and (listp type) - (member token-type type)) - (and (symbolp type) (eq token-type type)))) - ; test if symbol has embedded operator characters: - (cond ((and rslt suspicious-id-warn (eq token-type :id)) - (test-for-suspicious-symbol (car *sal-tokens*)))) - rslt)) - - -(defun maybe-parse-command () - (if (token-is '(:define :load :chdir :variable :function - ; :system - :play :print :display :plot)) - (parse-command) - (if (and (token-is '(:return)) *audacity-top-level-return-flag*) - (parse-command)))) - - -(defun parse-command () - (cond ((token-is '(:define :variable :function)) - (parse-declaration)) - ((token-is :load) - (parse-load)) - ((token-is :chdir) - (parse-chdir)) - ((token-is :play) - (parse-play)) -; ((token-is :system) -; (parse-system)) - ((token-is :print) - (parse-print-display :print 'sal-print)) - ((token-is :display) - (parse-print-display :display 'display)) - ((token-is :plot) - (parse-plot)) - ((and *audacity-top-level-return-flag* (token-is :return)) - (parse-return)) -; ((token-is :output) -; (parse-output)) - (t - (errexit "Command not found")))) - - -(defun parse-stmt () - (cond ((token-is :begin) - (parse-block)) - ((token-is '(:if :when :unless)) - (parse-conditional)) - ((token-is :set) - (parse-assignment)) - ((token-is :loop) - (parse-loop)) - ((token-is :print) - (parse-print-display :print 'sal-print)) - ((token-is :display) - (parse-print-display :display 'display)) - ((token-is :plot) - (parse-plot)) -; ((token-is :output) -; (parse-output)) - ((token-is :exec) - (parse-exec)) - ((token-is :exit) - (parse-exit)) - ((token-is :return) - (parse-return)) - ((token-is :load) - (parse-load)) - ((token-is :chdir) - (parse-chdir)) -; ((token-is :system) -; (parse-system)) - ((token-is :play) - (parse-play)) - (t - (errexit "Command not found")))) - - -;; GET-PARM-NAMES -- given parms like (a b &key (x 1) (y 2)), -;; return list of parameters: (a b x y) -(defun get-parm-names (parms) - (let (rslt) - (dolist (p parms) - (cond ((symbolp p) - (if (eq p '&key) nil (push p rslt))) - (t (push (car p) rslt)))) - (reverse rslt))) - - -;; RETURNIZE -- make a statement (list) end with a sal-return-from -;; -;; SAL returns nil from begin-end statement lists -;; -(defun returnize (stmt) - (let (rev expr) - (setf rev (reverse stmt)) - (setf expr (car rev)) ; last expression in list - (cond ((and (consp expr) (eq (car expr) 'sal-return-from)) - stmt) ; already ends in sal-return-from - (t - (reverse (cons (list 'sal-return-from *sal-fn-name* nil) - rev)))))) - - -(defun parse-declaration () - (if (token-is :define) (parse-token)) ; SAL extension: "define" is optional - (let (bindings setf-args formals parms stmt locals loc) - (cond ((token-is :variable) - (setf bindings (parse-bindings)) - (setf loc *rslt*) ; the "variable" token - (dolist (b bindings) - (cond ((symbolp b) - (push b setf-args) - (push `(if (boundp ',b) ,b) setf-args)) - (t - (push (first b) setf-args) - (push (second b) setf-args)))) - (add-line-info-to-stmt (cons 'setf (reverse setf-args)) loc)) - ((token-is :function) - (parse-token) - (if (token-is :id nil) - (setf *sal-fn-name* (token-lisp (parse-token))) - (errexit "function name expected here")) - (setf locals *sal-local-variables*) - (setf formals (parse-parms)) - (setf stmt (parse-stmt)) - ;; stmt may contain a return-from, so make this a progn or prog* - (cond ((and (consp stmt) - (not (eq (car stmt) 'progn)) - (not (eq (car stmt) 'prog*))) - (setf stmt (list 'progn stmt)))) - ;; need return to pop traceback stack - (setf stmt (returnize stmt)) - ;; get list of parameter names - (setf parms (get-parm-names formals)) - (setf *sal-local-variables* locals) - ;; build the defun - (prog1 (list 'defun *sal-fn-name* formals - (list 'sal-trace-enter - (list 'quote *sal-fn-name*) - (cons 'list parms) - (list 'quote parms)) - stmt) - (setf *sal-fn-name* nil))) - (t - (errexit "bad syntax"))))) - - -(defun parse-one-parm (kargs) - ;; kargs is a flag indicating previous parameter was a keyword (all - ;; the following parameters must then also be keyword parameters) - ;; returns: (<keyword> <default>) or (nil <identifier>) - ;; where <keyword> is a keyword parameter name (nil if not a keyword parm) - ;; <default> is an expression for the default value - ;; <identifier> is the parameter name (if not a keyword parm) - (let (key default-value id) - (cond ((and kargs (token-is :id)) - (errexit "positional parameter not allowed after keyword parameter")) - ((token-is :id) - ;(display "parse-one-1" (token-is :id) (car *sal-tokens*)) - (setf id (token-lisp (parse-token))) - (push id *sal-local-variables*) - (list nil id)) - ((token-is :key) - (setf key (sal-string-to-symbol (token-string (parse-token)))) - (cond ((or (token-is :co) (token-is :rp))) ; no default value - (t - (setf default-value (parse-sexpr)))) - (list key default-value)) - (kargs - (errexit "expected keyword name")) - (t - (errexit "expected parameter name"))))) - - -(defun parse-parms () - ;(display "parse-parms" *sal-tokens*) - (let (parms parm kargs expecting) - (if (token-is :lp) - (parse-token) ;; eat the left paren - (errexit "expected left parenthesis")) - (setf expecting (not (token-is :rp))) - (while expecting - (setf parm (parse-one-parm kargs)) - ;(display "parm" parm) - ;; returns list of (kargs . parm) - (if (and (car parm) (not kargs)) ; kargs just set - (push '&key parms)) - (setf kargs (car parm)) - ;; normally push the <id>; for keyword parms, push id and default value - (push (if kargs parm (cadr parm)) parms) - (if (token-is :co) - (parse-token) - (setf expecting nil))) - (if (token-is :rp) (parse-token) - (errexit "expected right parenthesis")) - ;(display "parse-parms" (reverse parms)) - (reverse parms))) - - -(defun parse-bindings () - (let (bindings bind) - (setf *rslt* (parse-token)) ; skip "variable" or "with" - ; return token as "extra" return value - (setf bind (parse-bind)) - (push (if (second bind) bind (car bind)) - bindings) - (while (token-is :co) - (parse-token) - (setf bind (parse-bind)) - ;; if non-nil initializer, push (id expr) - (push (if (second bind) bind (car bind)) - bindings)) - (reverse bindings))) - - -(defun parse-bind () - (let (id val) - (if (token-is :id nil) - (setf id (token-lisp (parse-token))) - (errexit "expected a variable name")) - (cond ((token-is :=) - (parse-token) - (setf val (parse-sexpr)))) - (push id *sal-local-variables*) - (list id val))) - - -(defun parse-chdir () - ;; assume next token is :chdir - (or (token-is :chdir) (error "parse-chdir internal error")) - (let (path loc) - (setf loc (parse-token)) - (setf path (parse-path)) - (add-line-info-to-stmt (list 'setdir path) loc))) - - -(defun parse-play () - ;; assume next token is :play - (or (token-is :play) (error "parse-play internal error")) - (let ((loc (parse-token))) - (add-line-info-to-stmt (list 'sal-play (parse-sexpr)) loc))) - - -(defun parse-return () - (or (token-is :return) (error "parse-return internal error")) - (let (loc expr) - ;; this seems to be a redundant test - (if (and (null *sal-fn-name*) - (not *audacity-top-level-return-flag*)) - (errexit "Return must be inside a function body")) - (setf loc (parse-token)) - (setf expr (parse-sexpr)) - (if *sal-fn-name* - (add-line-info-to-stmt (list 'sal-return-from *sal-fn-name* expr) loc) - (list 'defun 'main '() (list 'sal-trace-enter '(quote main) '() '()) - (add-line-info-to-stmt expr loc))))) - - -(defun parse-load () - ;; assume next token is :load - (or (token-is :load) (error "parse-load internal error")) - (let (path args loc) - (setf loc (parse-token)) - (setf path (parse-path)) ; must return path or raise error - (setf args (parse-keyword-args)) - (add-line-info-to-stmt (cons 'sal-load (cons path args)) loc))) - -(defun parse-keyword-args () - (let (args) - (while (token-is :co) - (parse-token) - (cond ((token-is :key) - (push (token-value) args) - (push (parse-sexpr) args)))) - (reverse args))) - - -'(defun parse-system () - ;; assume next token is :system - (or (token-is :system) (error "parse-system internal error")) - (let (path arg args) - (parse-token) - (setf path (parse-sexpr)) - (list 'sal-system path))) - - -(defun parse-path () - (if (token-is '(:id :string)) - (token-lisp (parse-token)) - (errexit "path not found"))) - - -(defun parse-print-display (token function) - ;; assumes next token is :print - (or (token-is token) (error "parse-print-display internal error")) - (let (args arg loc) - (setf loc (parse-token)) - (setf arg (parse-sexpr)) - (setf args (list arg)) - (while (token-is :co) - (parse-token) ; remove and ignore the comma - (setf arg (parse-sexpr)) - (push arg args)) - (add-line-info-to-stmt (cons function (reverse args)) loc))) - -(defun parse-plot () - ;; assumes next token is :plot - (or (token-is :plot) (error "parse-plot internal error")) - (let (arg args loc) - (setf loc (parse-token)) - (setf arg (parse-sexpr)) - (setf args (list arg)) - (cond ((token-is :co) ; get duration parameter - (parse-token) ; remove and ignore the comma - (setf arg (parse-sexpr)) - (push arg args) - (cond ((token-is :co) ; get n points parameter - (parse-token) ; remove and ignore the comma - (setf arg (parse-sexpr)))))) - (add-line-info-to-stmt (cons 's-plot (reverse args)) loc))) - -;(defun parse-output () -; ;; assume next token is :output -; (or (token-is :output) (error "parse-output internal error")) -; (parse-token) -; (list 'sal-output (parse-sexpr))) - - -(defun maybe-parse-block () - (if (token-is :begin) (parse-block))) - - -(defun parse-block () - ;; assumes next token is :block - (or (token-is :begin) (error "parse-block internal error")) - (let (args stmts (locals *sal-local-variables*)) - (parse-token) - (cond ((token-is :with) - (setf args (parse-bindings)))) - (while (not (token-is :end)) - (push (parse-stmt) stmts)) - (parse-token) - (setf stmts (reverse stmts)) - ;(display "parse-block" args stmts) - (setf *sal-local-variables* locals) - (cons 'prog* (cons args stmts)))) - - -;; MAKE-STATEMENT-LIST -- convert stmt to a stmt list -;; -;; if it is a (PROGN ...) then return cdr -- it's already a list -;; otherwise, put single statement into a list -;; -(defun make-statement-list (stmt) - (cond ((atom stmt) - (list stmt)) - ((eq (car stmt) 'progn) - (cdr stmt)) - (t - (list stmt)))) - -(setf *conditional-tokens* '(:if :when :unless)) - - -(defun maybe-parse-conditional () - (if (token-is *conditional-tokens*) - (parse-conditional))) - - -(defun parse-conditional () - ;; assumes next token is :if - (or (token-is *conditional-tokens*) - (error "parse-conditional internal error")) - (let (test then-stmt else-stmt if-token) - (cond ((token-is :if) - (setf if-token (parse-token)) - (setf test (parse-sexpr if-token)) - (if (not (token-is :then)) - (errexit "expected then after if")) - (parse-token) - (if (not (token-is :else)) ;; no then statement - (setf then-stmt (parse-stmt))) - (cond ((token-is :else) - (parse-token) - (setf else-stmt (parse-stmt)))) - ;(display "cond" test then-stmt else-stmt) - (if else-stmt - (list 'if test then-stmt else-stmt) - (list 'if test then-stmt))) - ((token-is :when) - (parse-token) - (setf test (parse-sexpr)) - (setf then-stmt (parse-stmt)) - (cons 'when (cons test (make-statement-list then-stmt)))) - ((token-is :unless) - (parse-token) - (setf test (parse-sexpr)) - (setf else-stmt (parse-stmt)) - (cons 'unless (cons test (make-statement-list else-stmt))))))) - - -(defun maybe-parse-assignment () - (if (token-is :set) (parse-assignment))) - - -(defun parse-assignment () - ;; first token must be set - (or (token-is :set) (error "parse-assignment internal error")) - (let (assignments rslt vref op expr set-token) - (setf set-token (parse-token)) - (push (parse-assign) assignments) ; returns (target op value) - (while (token-is :co) - (parse-token) ; skip the comma - (push (parse-assign) assignments)) - ; now assignments is ((target op value) (target op value)...) - (dolist (assign assignments) - (setf vref (first assign) op (second assign) expr (third assign)) - (cond ((eq op '=)) - ((eq op '-=) (setf expr `(diff ,vref ,expr))) - ((eq op '+=) (setf expr `(sum ,vref ,expr))) - ((eq op '*=) (setq expr `(mult ,vref ,expr))) - ((eq op '/=) (setq expr `(/ ,vref ,expr))) - ((eq op '&=) (setq expr `(nconc ,vref (list ,expr)))) - ((eq op '@=) (setq expr `(cons ,expr ,vref))) - ((eq op '^=) (setq expr `(nconc ,vref (append ,expr nil)))) - ((eq op '<=) (setq expr `(min ,vref ,expr))) - ((eq op '>=) (setq expr `(max ,vref ,expr))) - (t (errexit (format nil "unknown assignment operator ~A" op)))) - (push (list 'setf vref expr) rslt)) - (setf rslt (add-line-info-to-stmts rslt set-token)) - (if (> (length rslt) 1) - (cons 'progn rslt) - (car rslt)))) - - -;; PARSE-ASSIGN -- based on parse-bind, but with different operators -;; -;; allows arbitrary term on left because it could be an array -;; reference. After parsing, we can check that the target of the -;; assignment is either an identifier or an (aref ...) -;; -(defun parse-assign () - (let ((lhs (parse-term) op val)) - (cond ((token-is '(:= :-= :+= :*= :/= :&= :@= :^= :<= :>=)) - (setf op (parse-token)) - (setf op (if (eq (token-type op) ':=) '= (token-lisp op))) - (setf val (parse-sexpr)))) - (cond ((and (consp lhs) (eq (car lhs) 'aref))) ;; aref good - ((symbolp lhs)) ;; id good - (t (errexit "expected a variable name or array reference"))) - (list lhs op val))) - - -(defun maybe-parse-loop () - (if (token-is :loop) (parse-loop))) - - -;; loops are compiled to do* -;; bindings go next as usual, but bindings include for variables -;; and repeat is converted to a for +count+ from 0 to <sexpr> -;; stepping is done after statement -;; termination clauses are combined with OR and -;; finally goes after termination -;; statement goes in do* body -;; -(defun parse-loop () - (or (token-is :loop) (error "parse-loop: internal error")) - (let (bindings termination-tests stmts sexpr rslt finally - loc - (locals *sal-local-variables*)) - (parse-token) ; skip "loop" - (if (token-is :with) - (setf bindings (reverse (parse-bindings)))) - (while (token-is '(:repeat :for)) - (cond ((token-is :repeat) - (setf loc (parse-token)) - (push (list 'sal:loopcount 0 '(1+ sal:loopcount)) bindings) - (setf sexpr (parse-sexpr loc)) ; get final count expression - (push (list 'sal:loopfinal sexpr) bindings) - (push '(>= sal:loopcount sal:loopfinal) termination-tests)) - ((token-is :for) - (setf rslt (parse-for-clause)) - ; there can be multiple bindings, build bindings in reverse - (cond ((first rslt) - (setf bindings (append (reverse (first rslt)) - bindings)))) - (if (second rslt) (push (second rslt) termination-tests))))) - (while (token-is '(:while :until)) - (cond ((token-is :while) - (setf loc (parse-token)) - (push (list 'not (parse-sexpr loc)) termination-tests)) - ((token-is :until) - (setf loc (parse-token)) - (push (parse-sexpr loc) termination-tests)))) - ; (push (parse-stmt) stmts) - (while (not (token-is '(:end :finally))) - (push (parse-stmt) stmts)) - (cond ((token-is :finally) - (parse-token) ; skip "finally" - (setf finally (parse-stmt)))) - (if (token-is :end) - (parse-token) - (errexit "expected end after loop")) - (setf *sal-local-variables* locals) - `(do* ,(reverse bindings) - ,(list (or-ize (reverse termination-tests)) finally) - ,@(reverse stmts)))) - - -;; OR-IZE -- compute the OR of a list of expressions -;; -(defun or-ize (exprs) - (if (> (length exprs) 1) (cons 'or exprs) - (car exprs))) - - -(defun maybe-parse-exec () - (if (token-is :exec) (parse-exec))) - - -(defun parse-exec () - (or (token-is :exec) (error "parse-exec internal error")) - (let ((loc (parse-token))) ; skip the :exec - (parse-sexpr loc))) - - -(defun maybe-parse-exit () - (if (token-is :exit) (parse-exit))) - - -(defun parse-exit () - (let (tok loc) - (or (token-is :exit) (error "parse-exit internal error")) - (setf loc (parse-token)) ; skip the :exit - (cond ((token-is :id) - (setf tok (parse-token)) - (cond ((eq (token-lisp tok) 'nyquist) - (add-line-info-to-stmt '(exit) loc)) - ((eq (token-lisp tok) 'sal) - (add-line-info-to-stmt '(sal-exit) loc)) - (t - (errexit "expected \"nyquist\" or \"sal\" after \"exit\"")))) - (t - (add-line-info-to-stmt '(sal-exit) loc))))) - - -;; PARSE-FOR-CLAUSE - returns (bindings term-test) -;; -(defun parse-for-clause () - (or (token-is :for) (error "parse-for-clause: internal error")) - (let (id init next rslt binding term-test list-id loc) - (setf loc (parse-token)) ; skip for - (if (token-is :id) - (setf id (token-lisp (parse-token))) - (errexit "expected identifier after for")) - (cond ((token-is :=) - ;; if the clause is just for id = expr, then assume that - ;; expr depends on something that changes each iteration: - ;; recompute and assign expr to id each time around - (parse-token) ; skip "=" - (setf init (parse-sexpr loc)) - (cond ((token-is :then) - (parse-token) ; skip "then" - (setf binding (list id init (parse-sexpr loc)))) - (t - (setf binding (list id init init)))) - (setf binding (list binding))) - ((token-is :in) - (setf loc (parse-token)) ; skip "in" - (setf list-id (intern (format nil "SAL:~A-LIST" id))) - (setf binding - (list (list list-id (parse-sexpr loc) - (list 'cdr list-id)) - (list id (list 'car list-id) (list 'car list-id)))) - (setf term-test (list 'null list-id))) - ((token-is :over) - (setf loc (parse-token)) ; skip "over" - (setf start (parse-sexpr loc)) -#| (cond ((token-is :by) - (parse-token) ; skip "by" - (parse-sexpr))) ;-- I don't know what "by" means - RBD |# - (setf list-id (intern (format nil "SAL:~A-PATTERN" id))) - (setf binding - (list (list list-id start) - (list id (list 'next list-id) (list 'next list-id))))) - ((token-is '(:from :below :to :above :downto :by)) - (cond ((token-is :from) - (setf loc (parse-token)) ; skip "from" - (setf init (parse-sexpr loc))) - (t - (setf init 0))) - (cond ((token-is :below) - (setf loc (parse-token)) ; skip "below" - (setf term-test (list '>= id (parse-sexpr loc)))) - ((token-is :to) - (setf loc (parse-token)) ; skip "to" - (setf term-test (list '> id (parse-sexpr loc)))) - ((token-is :above) - (setf loc (parse-token)) ; skip "above" - (setf term-test (list '<= id (parse-sexpr loc)))) - ((token-is :downto) - (setf loc (parse-token)) ; skip "downto" - (setf term-test (list '< id (parse-sexpr loc))))) - (cond ((token-is :by) - (setf loc (parse-token)) ; skip "by" - (setf binding (list id init (list '+ id (parse-sexpr loc))))) - ((or (null term-test) - (and term-test (member (car term-test) '(>= >)))) - (setf binding (list id init (list '1+ id)))) - (t ; loop goes down because of "above" or "downto" - ; (display "for step" term-test) - (setf binding (list id init (list '1- id))))) - (setf binding (list binding))) - (t - (errexit "for statement syntax error"))) - (list binding term-test))) - - -;; parse-sexpr works by building a list: (term op term op term ...) -;; later, the list is parsed again using operator precedence rules -(defun parse-sexpr (&optional loc) - (let (term rslt) - (push (parse-term) rslt) - (while (token-is *sal-operators*) - (push (token-type (parse-token)) rslt) - (push (parse-term) rslt)) - (setf rslt (reverse rslt)) - ;(display "parse-sexpr before inf->pre" rslt) - (setf rslt (if (consp (cdr rslt)) - (inf->pre rslt) - (car rslt))) - (if loc - (setf rslt (add-line-info-to-expression rslt loc))) - rslt)) - - -(defun get-lisp-op (op) - (third (assoc op +operators+))) - - -;; a term is <unary-op> <term>, or -;; ( <sexpr> ), or -;; ? ( <sexpr> , <sexpr> , <sexpr> ), or -;; <id>, or -;; <id> ( <args> ), or -;; <term> [ <sexpr> ] -;; Since any term can be followed by indexing, handle everything -;; but the indexing here in parse-term-1, then write parse-term -;; to do term-1 followed by indexing operations -;; -(defun parse-term-1 () - (let (sexpr id vars loopvar n) - (cond ((token-is '(:- :!)) - (list (token-lisp (parse-token)) (parse-term))) - ((token-is :lp) - (parse-token) ; skip left paren - (setf sexpr (parse-sexpr)) - (if (token-is :rp) - (parse-token) - (errexit "right parenthesis not found")) - sexpr) - ((token-is :?) - (parse-ifexpr)) - ((token-is :lc) - (list 'quote (parse-list))) - ((token-is '(:int :float :bool :list :string)) - ;(display "parse-term int float bool list string" (car *sal-tokens*)) - (token-lisp (parse-token))) - ((token-is :id) ;; aref or funcall - (setf id (token-lisp (parse-token))) - ;; array indexing was here, but that only allows [x] after - ;; identifiers. Move this to expression parsing. - (cond ((token-is :lp) - (parse-token) - (setf sexpr (cons id (parse-pargs t))) - (if (token-is :rp) - (parse-token) - (errexit "right paren not found")) - sexpr) - (t id))) - ((token-is '(:seqv :seqrepv)) - (setf id (intern (string-upcase (token-string (parse-token))))) - (display "parse-term-1" id) - (setf vars (parse-idlist)) - (if (not (token-is :lp)) - (errexit "expected list of behaviors")) - (parse-token) - (setf sexpr (parse-pargs nil)) - ;; if this is seqrepv, move the first 2 parameters (loop var and - ;; count expression) in front of the var list - (cond ((eq id 'SEQREPV) - (setf loopvar (pop sexpr)) - (if (not (and loopvar (symbolp loopvar))) - (errexit "expected identifier as first \"parameter\"")) - (setf n (pop sexpr)) - (if (null n) - (errexit "expected repetition count as second parameter")) - (setf vars (cons id (cons n vars))))) - (setf sexpr (cons id (cons vars sexpr))) - (if (token-is :rp) - (parse-token) - (errexit "right paren not found")) - sexpr) - (t - (errexit "expression not found"))))) - - -(defun parse-idlist () - ; similar to parse-parms, but simpler because no keywords and default vals - (let (parms parm kargs expecting) - (if (token-is :lp) (parse-token) ;; eat the left paren - (errexit "expected left parenthesis")) - (setf expecting (not (token-is :rp))) - (while expecting - (if (token-is :id) - (push (token-lisp (parse-token)) parms) - (errexit "expected variable name")) - (if (token-is :co) (parse-token) - (setf expecting nil))) - (if (token-is :rp) (parse-token) - (errexit "expected right parenthesis")) - (reverse parms))) - - -(defun parse-term () - (let ((term (parse-term-1))) - ; (display "parse-term" term (token-is :lb)) - (while (token-is :lb) - (parse-token) - (setf term (list 'aref term (parse-sexpr))) - (if (token-is :rb) - (parse-token) - (errexit "right bracket not found"))) - term)) - - -(defun parse-ifexpr () - (or (token-is :?) (error "parse-ifexpr internal error")) - (let (condition then-sexpr else-sexpr) - (parse-token) ; skip the :? - (if (token-is :lp) (parse-token) (errexit "expected left paren")) - (setf condition (parse-sexpr)) - (if (token-is :co) (parse-token) (errexit "expected comma")) - (setf then-sexpr (parse-sexpr)) - (if (token-is :co) (parse-token) (errexit "expected comma")) - (setf else-sexpr (parse-sexpr)) - (if (token-is :rp) (parse-token) (errexit "expected left paren")) - (list 'if condition then-sexpr else-sexpr))) - - -(defun keywordp (s) - (and (symbolp s) (eq (type-of (symbol-name s)) 'string) - (equal (char (symbol-name s) 0) #\:))) - - -(defun functionp (x) (eq (type-of x) 'closure)) - - -(defun parse-pargs (keywords-allowed) - ;; get a list of sexprs. If keywords-allowed, then at any point - ;; the arg syntax can switch from [<co> <sexpr>]* to - ;; [<co> <keyword> <sexpr>]* - ;; also if keywords-allowed, it's a function call and the - ;; list may be empty. Otherwise, it's a list of indices and - ;; the list may not be empty - (let (pargs keyword-expected sexpr keyword) - (if (and keywords-allowed (token-is :rp)) - nil ; return empty parameter list - (loop ; look for one or more [keyword] sexpr - ; optional keyword test - (setf keyword nil) - ; (display "pargs" (car *sal-tokens*)) - (if (token-is :key) - (setf keyword (token-lisp (parse-token)))) - ; (display "parse-pargs" keyword) - ; did we need a keyword? - (if (and keyword-expected (not keyword)) - (errexit "expected keyword")) - ; was a keyword legal - (if (and keyword (not keywords-allowed)) - (errexit "keyword not allowed here")) - (setf keyword-expected keyword) ; once we get a keyword, we need - ; one before each sexpr - ; now find sexpr - (setf sexpr (parse-sexpr)) - (if keyword (push keyword pargs)) - (push sexpr pargs) - ; (display "parse-pargs" keyword sexpr pargs) - (cond ((token-is :co) - (parse-token)) - (t - (return (reverse pargs)))))))) - - -;; PARSE-LIST -- parse list in braces {}, return list not quoted list -;; -(defun parse-list () - (or (token-is :lc) (error "parse-list internal error")) - (let (elts) - (parse-token) - (while (not (token-is :rc)) - (cond ((token-is '(:int :float :id :bool :key :string)) - (push (token-lisp (parse-token)) elts)) - ((token-is *sal-operators*) - (push (intern (token-string (parse-token))) elts)) - ((token-is :lc) - (push (parse-list) elts)) - ((token-is :co) - (errexit "expected list element or right brace; do not use commas inside braces {}")) - (t - (errexit "expected list element or right brace")))) - (parse-token) - (reverse elts))) - - -(defparameter *op-weights* - '( - (:\| 1) - (:& 2) - (:! 3) - (:= 4) - (:!= 4) - (:> 4) - (:>= 4) - (:< 4) - (:<= 4) - (:~= 4) ; general equality - (:+ 5) - (:- 5) - (:% 5) - (:* 6) - (:/ 6) - (:^ 7) - (:~ 8) - (:~~ 8) - (:@ 8) - (:@@ 8))) - - -(defun is-op? (x) - ;; return op weight if x is operator - (let ((o (assoc (if (listp x) (token-type x) x) - *op-weights*))) - (and o (cadr o)))) - - -(defun inf->pre (inf) - ;; this does NOT rewrite subexpressions because parser applies rules - ;; depth-first so subexprs are already processed - (let (op lh rh w1) - (if (consp inf) - (do () - ((null inf) lh) - (setq op (car inf)) ; look at each element of in - (pop inf) - (setq w1 (is-op? op)) - (cond ((numberp w1) ; found op (w1 is precedence) - (do ((w2 nil) - (ok t) - (li (list))) - ((or (not inf) (not ok)) - (setq rh (inf->pre (nreverse li))) - (setq lh (if lh (list (get-lisp-op op) lh rh) - (list (get-lisp-op op) rh nil)))) - (setq w2 (is-op? (first inf))) - (cond ((and w2 (<= w2 w1)) - (setq ok nil)) - (t - (push (car inf) li) - (pop inf))))) - (t - (setq lh op)))) - inf))) - diff --git a/Release/nyquist/sal.lsp b/Release/nyquist/sal.lsp deleted file mode 100644 index cbb451b1f268ad81a26ee0641316dcdf2f4c9689..0000000000000000000000000000000000000000 --- a/Release/nyquist/sal.lsp +++ /dev/null @@ -1,630 +0,0 @@ -;;; ********************************************************************** -;;; Copyright (C) 2006 Rick Taube -;;; This program is free software; you can redistribute it and/or -;;; modify it under the terms of the Lisp Lesser Gnu Public License. -;;; See http://www.cliki.net/LLGPL for the text of this agreement. -;;; ********************************************************************** - -;;; $Revision: 1.2 $ -;;; $Date: 2009-03-05 17:42:25 $ - -;; DATA STRUCTURES AND ALGORITHMS (for sal.lsp and parse.lsp) -;; -;; TOKENIZE converts source language (a string) into a list of tokens -;; each token is represented as follows: -;; (:TOKEN <type> <string> <start> <info> <lisp>) -;; where <type> is one of: -;; :id -- an identifier -;; :lp -- left paren -;; :rp -- right paren -;; :+, etc. -- operators -;; :int -- an integer -;; :float -- a float -;; :print, etc. -- a reserved word -;; <string> is the source string for the token -;; <start> is the column of the string -;; <info> and <lisp> are ?? -;; Tokenize uses a list of reserved words extracted from terminals in -;; the grammar. Each reserved word has an associated token type, but -;; all other identifiers are simply of type :ID. -;; -;; *** WHY REWRITE THE ORIGINAL PARSER? *** -;; Originally, the code interpreted a grammar using a recursive pattern -;; matcher, but XLISP does not have a huge stack and there were -;; stack overflow problems because even relatively small expressions -;; went through a very deep nesting of productions. E.g. -;; "print note(between(30,odds(.5, 60, 90)))" 0 t nil))" was at recursion -;; level 46 when the stack overflowed. The stack depth is 2000 or 4000, -;; but all locals and parameters get pushed here, so since PARSE is the -;; recursive function and it has lots of parameters and locals, it appears -;; to use 80 elements in the stack per call. -;; *** END *** -;; -;; The grammar for the recursive descent parser: -;; note: [ <x> ] means optional <x>, <x>* means 0 or more of <x> -;; -;; <number> = <int> | <float> -;; <atom> = <int> | <float> | <id> | <bool> -;; <list> = { <elt>* } -;; <elt> = <atom> | <list> | <string> -;; <aref> = <id> <lb> <pargs> <rb> -;; <ifexpr> = ? "(" <sexpr> , <sexpr> [ , <sexpr> ] ")" -;; <funcall> = <id> <funargs> -;; <funargs> = "(" [ <args> ] ")" -;; <args> = <arg> [ , <arg> ]* -;; <arg> = <sexpr> | <key> <sexpr> -;; <op> = + | - | "*" | / | % | ^ | = | != | -;; "<" | ">" | "<=" | ">=" | ~= | ! | & | "|" -;; <mexpr> = <term> [ <op> <term> ]* -;; <term> = <-> <term> | <!> <term> | "(" <mexpr> ")" | -;; <ifexpr> | <funcall> | <aref> | <atom> | <list> | <string> -;; <sexpr> = <mexpr> | <object> | class -;; <top> = <command> | <block> | <conditional> | <assignment> | <loop> | <exec> -;; <exec> = exec <sexpr> -;; <command> = <define-cmd> | <file-cmd> | <output> -;; <define-cmd> = define <declaration> -;; <declaration> = <vardecl> | <fundecl> -;; <vardecl> = variable <bindings> -;; <bindings> = <bind> [ , <bind> ]* -;; <bind> = <id> [ <=> <sexpr> ] -;; <fundecl> = <function> <id> "(" [ <parms> ] ")" <statement> -;; <parms> = <parm> [ , <parm> ]* -;; this is new: key: expression for keyword parameter -;; <parm> = <id> | <key> [ <sexpr> ] -;; <statement> = <block> | <conditional> | <assignment> | -;; <output-stmt> <loop-stmt> <return-from> | <exec> -;; <block> = begin [ with <bindings> [ <statement> ]* end -;; <conditional> = if <sexpr> then [ <statement> ] [ else <statement> ] | -;; when <sexpr> <statement> | unless <sexpr> <statement> -;; <assignment> = set <assign> [ , <assign> ]* -;; <assign> = ( <aref> | <id> ) <assigner> <sexpr> -;; <assigner> = = | += | *= | &= | @= | ^= | "<=" | ">=" -;; <file-cmd> = <load-cmd> | chdir <pathref> | -;; system <pathref> | play <sexpr> -;; (note: system was removed) -;; <load-cmd> = load <pathref> [ , <key> <sexpr> ]* -;; <pathref> = <string> | <id> -;; <output-stmt> = print <sexpr> [ , <sexpr> ]* | -;; output <sexpr> -;; <loop-stmt> = loop [ with <bindings> ] [ <stepping> ]* -;; [ <termination> ]* [ <statement> ]+ -;; [ finally <statement> ] end -;; <stepping> = repeat <sexpr> | -;; for <id> = <sexpr> [ then <sexpr> ] | -;; for <id> in <sexpr> | -;; for <id> over <sexpr> [ by <sexpr> ] | -;; for <id> [ from <sexpr> ] -;; [ ( below | to | above | downto ) <sexpr> ] -;; [ by <sexpr> ] | -;; <termination> = while <sexpr> | until <sexpr> -;; <return-from> = return <sexpr> - -;(in-package cm) - -; (progn (cd "/Lisp/sal/") (load "parse.lisp") (load "sal.lisp")) - -(setfn defconstant setf) -(setfn defparameter setf) -(setfn defmethod defun) -(setfn defvar setf) -(setfn values list) -(if (not (boundp '*sal-secondary-prompt*)) - (setf *sal-secondary-prompt* t)) -(if (not (boundp '*sal-xlispbreak*)) - (setf *sal-xlispbreak* nil)) - -(defun sal-trace-enter (fn &optional argvals argnames) - (push (list fn *sal-line* argvals argnames) *sal-call-stack*)) - -(defun sal-trace-exit () - (setf *sal-line* (second (car *sal-call-stack*))) - (pop *sal-call-stack*)) - -;; SAL-RETURN-FROM is generated by Sal compiler and -;; performs a return as well as a sal-trace-exit() -;; -(defmacro sal-return-from (fn val) - `(prog ((sal:return-value ,val)) - (setf *sal-line* (second (car *sal-call-stack*))) - (pop *sal-call-stack*) - (return-from ,fn sal:return-value))) - - -(setf *sal-traceback* t) - - -(defun sal-traceback (&optional (file t) - &aux comma name names line) - (format file "Call traceback:~%") - (setf line *sal-line*) - (dolist (frame *sal-call-stack*) - (setf comma "") - (format file " ~A" (car frame)) - (cond ((symbolp (car frame)) - (format file "(") - (setf names (cadddr frame)) - (dolist (arg (caddr frame)) - (setf name (car names)) - (format file "~A~% ~A = ~A" comma name arg) - (setf names (cdr names)) - (setf comma ",")) - (format file ") at line ~A~%" line) - (setf line (second frame))) - (t - (format file "~%"))))) - - -'(defmacro defgrammer (sym rules &rest args) - `(defparameter ,sym - (make-grammer :rules ',rules ,@args))) - -'(defun make-grammer (&key rules literals) - (let ((g (list 'a-grammer rules literals))) - (grammer-initialize g) - g)) - -'(defmethod grammer-initialize (obj) - (let (xlist) - ;; each literal is (:name "name") - (cond ((grammer-literals obj) - (dolist (x (grammer-literals obj)) - (cond ((consp x) - (push x xlist)) - (t - (push (list (string->keyword (string-upcase (string x))) - (string-downcase (string x))) - xlist))))) - (t - (dolist (x (grammer-rules obj)) - (cond ((terminal-rule? x) - (push (list (car x) - (string-downcase (subseq (string (car x)) 1))) - xlist)))))) - (set-grammer-literals obj (reverse xlist)))) - -'(setfn grammer-rules cadr) -'(setfn grammer-literals caddr) -'(defun set-grammer-literals (obj val) - (setf (car (cddr obj)) val)) -'(defun is-grammer (obj) (and (consp obj) (eq (car obj) 'a-grammer))) - -(defun string->keyword (str) - (intern (strcat ":" (string-upcase str)))) - -(defun terminal-rule? (rule) - (or (null (cdr rule)) (not (cadr rule)))) - -(load "sal-parse.lsp" :verbose nil) - -(defparameter *sal-print-list* t) - -(defun sal-printer (x &key (stream *standard-output*) (add-space t) - (in-list nil)) - (let ((*print-case* ':downcase)) - (cond ((and (consp x) *sal-print-list*) - (write-char #\{ stream) - (do ((items x (cdr items))) - ((null items)) - (sal-printer (car items) :stream stream - :add-space (cdr items) :in-list t) - (cond ((cdr items) - (cond ((not (consp (cdr items))) - (princ "<list not well-formed> " stream) - (sal-printer (cdr items) :stream stream :add-space nil) - (setf items nil)))))) - (write-char #\} stream)) - ((not x) (princ "#f" stream) ) - ((eq x t) (princ "#t" stream)) - (in-list (prin1 x stream)) - (t (princ x stream))) - (if add-space (write-char #\space stream)))) - -(defparameter *sal-printer* #'sal-printer) - -(defun sal-message (string &rest args) - (format t "~&; ") - (apply #'format t string args)) - - -;; sal-print has been modified from the original SAL to print items separated -;; by spaces (no final trailing space) and followed by a newline. -(defun sal-print (&rest args) - (do ((items args (cdr items))) - ((null items)) - ;; add space unless we are at the last element - (funcall *sal-printer* (car items) :add-space (cdr items))) - (terpri) - (values)) - -(defmacro keyword (sym) - `(str-to-keyword (symbol-name ',sym))) - -(defun plus (&rest nums) - (apply #'+ nums)) - -(defun minus (num &rest nums) - (apply #'- num nums)) - -(defun times (&rest nums) - (apply #'* nums)) - -(defun divide (num &rest nums) - (apply #'/ num nums)) - -;; implementation of infix "!=" operator -(defun not-eql (x y) - (not (eql x y))) - -; dir "*.* -; chdir -; load "rts.sys" - -(defun sal-chdir ( dir) - (cd (expand-path-name dir)) - (sal-message "Directory: ~A" (pwd)) - (values)) - -;;; sigh, not all lisps support ~/ directory components. - -(defun expand-path-name (path &optional absolute?) - (let ((dir (pathname-directory path))) - (flet ((curdir () - (truename - (make-pathname :directory - (pathname-directory - *default-pathname-defaults*))))) - (cond ((null dir) - (if (equal path "~") - (namestring (user-homedir-pathname)) - (if absolute? - (namestring (merge-pathnames path (curdir))) - (namestring path)))) - ((eql (car dir) ':absolute) - (namestring path)) - (t - (let* ((tok (second dir)) - (len (length tok))) - (if (char= (char tok 0) #\~) - (let ((uhd (pathname-directory (user-homedir-pathname)))) - (if (= len 1) - (namestring - (make-pathname :directory (append uhd (cddr dir)) - :defaults path)) - (namestring - (make-pathname :directory - (append (butlast uhd) - (list (subseq tok 1)) - (cddr dir)) - :defaults path)))) - (if absolute? - (namestring (merge-pathnames path (curdir))) - (namestring path))))))))) - - -(defun sal-load (filename &key (verbose t) print) - (progv '(*sal-input-file-name*) (list filename) - (prog (file extended-name) - ;; first try to load exact name - (cond ((setf file (open filename)) - (close file) ;; found it: close it and load it - (return (generic-loader filename verbose print)))) - ;; try to load name with ".sal" or ".lsp" - (cond ((string-search "." filename) ; already has extension - nil) ; don't try to add another extension - ((setf file (open (strcat filename ".sal"))) - (close file) - (return (sal-loader (strcat filename ".sal") - :verbose verbose :print print))) - ((setf file (open (strcat filename ".lsp"))) - (close file) - (return (lisp-loader filename :verbose verbose :print print)))) - ;; search for file as is or with ".lsp" on path - (setf fullpath (find-in-xlisp-path filename)) - (cond ((and (not fullpath) ; search for file.sal on path - (not (string-search "." filename))) ; no extension yet - (setf fullpath (find-in-xlisp-path (strcat filename ".sal"))))) - (cond ((null fullpath) - (format t "sal-load: could not find ~A~%" filename)) - (t - (return (generic-loader fullpath verbose print))))))) - - -;; GENERIC-LOADER -- load a sal or lsp file based on extension -;; -;; assumes that file exists, and if no .sal extension, type is Lisp -;; -(defun generic-loader (fullpath verbose print) - (cond ((has-extension fullpath ".sal") - (sal-loader fullpath :verbose verbose :print print)) - (t - (lisp-loader fullpath :verbose verbose :print print)))) - -#| -(defun sal-load (filename &key (verbose t) print) - (progv '(*sal-input-file-name*) (list filename) - (let (file extended-name) - (cond ((has-extension filename ".sal") - (sal-loader filename :verbose verbose :print print)) - ((has-extension filename ".lsp") - (lisp-load filename :verbose verbose :print print)) - ;; see if we can just open the exact filename and load it - ((setf file (open filename)) - (close file) - (lisp-load filename :verbose verbose :print print)) - ;; if not, then try loading file.sal and file.lsp - ((setf file (open (setf *sal-input-file-name* - (strcat filename ".sal")))) - (close file) - (sal-loader *sal-input-file-name* :verbose verbose :print print)) - ((setf file (open (setf *sal-input-file-name* - (strcat filename ".lsp")))) - (close file) - (lisp-load *sal-input-file-name* :verbose verbose :print print)) - (t - (format t "sal-load: could not find ~A~%" filename)))))) -|# - -(defun lisp-loader (filename &key (verbose t) print) - (if (load filename :verbose verbose :print print) - t ; be quiet if things work ok - (format t "error loading lisp file ~A~%" filename))) - - -(defun has-extension (filename ext) - (let ((loc (string-search ext filename - :start (max 0 (- (length filename) - (length ext)))))) - (not (null loc)))) ; coerce to t or nil - - -(defmacro sal-at (s x) (list 'at x s)) -(defmacro sal-at-abs (s x) (list 'at-abs x s)) -(defmacro sal-stretch (s x) (list 'stretch x s)) -(defmacro sal-stretch-abs (s x) (list 'stretch-abs x s)) - -;; splice every pair of lines -(defun strcat-pairs (lines) - (let (rslt) - (while lines - (push (strcat (car lines) (cadr lines)) rslt) - (setf lines (cddr lines))) - (reverse rslt))) - - -(defun strcat-list (lines) - ;; like (apply 'strcat lines), but does not use a lot of stack - ;; When there are too many lines, XLISP will overflow the stack - ;; because args go on the stack. - (let (r) - (while (> (setf len (length lines)) 1) - (if (oddp len) (setf lines (cons "" lines))) - (setf lines (strcat-pairs lines))) - ; if an empty list, return "", else list has one string: return it - (if (null lines) "" (car lines)))) - - -(defun sal-loader (filename &key verbose print) - (let ((input "") (file (open filename)) line lines) - (cond (file - (push filename *loadingfiles*) - (while (setf line (read-line file)) - (push line lines) - (push "\n" lines)) - (close file) - (setf input (strcat-list (reverse lines))) - (sal-trace-enter (strcat "Loading " filename)) - (sal-compile input t t filename) - (pop *loadingfiles*) - (sal-trace-exit)) - (t - (format t "error loading SAL file ~A~%" filename))))) - - -; SYSTEM command is not implemented -;(defun sal-system (sys &rest pairs) -; (apply #'use-system sys pairs)) - - -(defun load-sal-file (file) - (with-open-file (f file :direction :input) - (let ((input (make-array '(512) :element-type 'character - :fill-pointer 0 :adjustable t))) - (loop with flag - for char = (read-char f nil ':eof) - until (or flag (eql char ':eof)) - do - (when (char= char #\;) - (loop do (setq char (read-char f nil :eof)) - until (or (eql char :eof) - (char= char #\newline)))) - (unless (eql char ':eof) - (vector-push-extend char input))) - (sal input :pattern :command-sequence)))) - - -(defmacro sal-play (snd) - (if (stringp snd) `(play-file ,snd) - `(play ,snd))) - - -(if (not (boundp '*sal-compiler-debug*)) - (setf *sal-compiler-debug* nil)) - - -(defmacro sal-simrep (variable iterations body) - `(simrep (,variable ,iterations) ,body)) - - -(defmacro sal-seqrep (variable iterations body) - `(seqrep (,variable ,iterations) ,body)) - - -;; function called in sal programs to exit the sal read-compile-run-print loop -(defun sal-exit () (setf *sal-exit* t)) - -(setf *sal-call-stack* nil) - -;; read-eval-print loop for sal commands -(defun sal () - (progv '(*breakenable* *tracenable* *sal-exit* *sal-mode*) - (list *sal-break* *xlisp-traceback* nil t) - (let (input line) - (setf *sal-call-stack* nil) - (read-line) ; read the newline after the one the user - ; typed to invoke this fn - (princ "Entering SAL mode ...\n"); - (while (not *sal-exit*) - (princ "\nSAL> ") - (sal-trace-enter "SAL top-level command interpreter") - ;; get input terminated by two returns - (setf input "") - (while (> (length (setf line (read-line))) 0) - (if *sal-secondary-prompt* (princ " ... ")) - (setf input (strcat input "\n" line))) - ;; input may have an extra return, remaining from previous read - ;; if so, trim it because it affects line count in error messages - (if (and (> (length input) 0) (char= (char input 0) #\newline)) - (setf input (subseq input 1))) - (sal-compile input t nil "<console>") - (sal-trace-exit)) - (princ "Returning to Lisp ...\n"))) - ;; in case *xlisp-break* or *xlisp-traceback* was set from SAL, impose - ;; them here - (cond ((not *sal-mode*) - (setf *breakenable* *xlisp-break*) - (setf *tracenable* *xlisp-traceback*))) - t) - - - -(defun sal-error-output (stack) - (if *sal-traceback* (sal-traceback)) - (setf *sal-call-stack* stack)) ;; clear the stack - - -;; when true, top-level return statement is legal and compiled into MAIN -(setf *audacity-top-level-return-flag* nil) - -;; SAL-COMPILE-AUDACITY -- special treatment of RETURN -;; -;; This works like SAL-COMPILE, but if there is a top-level -;; return statement (not normally legal), it is compiled into -;; a function named MAIN. This is a shorthand for Audacity plug-ins -;; -(defun sal-compile-audacity (input eval-flag multiple-statements filename) - (progv '(*audacity-top-level-return-flag*) '(t) - (sal-compile input eval-flag multiple-statements filename))) - - -;; SAL-COMPILE -- translate string or token list to lisp and eval -;; -;; input is either a string or a token list -;; eval-flag tells whether to evaluate the program or return the lisp -;; multiple-statements tells whether the input can contain multiple -;; top-level units (e.g. from a file) or just one (from command line) -;; returns: -;; if eval-flag, then nothing is returned -;; otherwise, returns nil if an error is encountered -;; otherwise, returns a list (PROGN p1 p2 p3 ...) where pn are lisp -;; expressions -;; -;; Note: replaced local variables here with "local" names to avoid -;; collisions with globals that compiled code might try to use: -;; eval uses local bindings, not global ones -;; -(defun sal-compile (sal:input sal:evflag sal:mult-stmts sal:filename) - ;; save some globals because eval could call back recursively - (progv '(*sal-tokens* *sal-input* *sal-input-text*) '(nil nil nil) - (let (sal:output sal:remainder sal:rslt sal:stack) - (setf sal:stack *sal-call-stack*) - ;; if first input char is "(", then eval as a lisp expression: - ;(display "sal-compile" sal:input)(setf *sal-compiler-debug* t) - (cond ((input-starts-with-open-paren sal:input) - ;(print "sal:input is lisp expression") - (errset - (print (eval (read (make-string-input-stream sal:input)))) t)) - (t ;; compile SAL expression(s): - (loop - (setf sal:output (sal-parse nil nil sal:input sal:mult-stmts - sal:filename)) - (cond ((first sal:output) ; successful parse - (setf sal:remainder *sal-tokens*) - (setf sal:output (second sal:output)) - (when *sal-compiler-debug* - (terpri) - (pprint sal:output)) - (cond (sal:evflag ;; evaluate the compiled code - (cond ((null (errset (eval sal:output) t)) - (sal-error-output sal:stack) - (return)))) ;; stop on error - (t - (push sal:output sal:rslt))) - ;(display "sal-compile after eval" - ; sal:remainder *sal-tokens*) - ;; if there are statements left over, maybe compile again - (cond ((and sal:mult-stmts sal:remainder) - ;; move sal:remainder to sal:input and iterate - (setf sal:input sal:remainder)) - ;; see if we've compiled everything - ((and (not sal:evflag) (not sal:remainder)) - (return (cons 'progn (reverse sal:rslt)))) - ;; if eval but no more sal:input, return - ((not sal:remainder) - (return)))) - (t ; error encountered - (return))))))))) - -;; SAL just evaluates lisp expression if it starts with open-paren, -;; but sometimes reader reads previous newline(s), so here we -;; trim off initial newlines and check if first non-newline is open-paren -(defun input-starts-with-open-paren (input) - (let ((i 0)) - (while (and (stringp input) - (> (length input) i) - (eq (char input i) #\newline)) - (incf i)) - (and (stringp input) - (> (length input) i) - (eq (char input i) #\()))) - -(defun sal-list-equal (a b) - (let ((rslt t)) ;; set to false if any element not equal - (dolist (x a) - (if (sal-equal x (car b)) - t ;; continue comparing - (return (setf rslt nil))) ;; break out of loop - (setf b (cdr b))) - (and rslt (null b)))) ;; make sure no leftovers in b - - -(defun sal-plus(a b &optional (source "+ operation in SAL")) - (ny:typecheck (not (or (numberp a) (soundp a) (multichannel-soundp a))) - (ny:error source 0 number-sound-anon a t)) - (ny:typecheck (not (or (numberp b) (soundp b) (multichannel-soundp b))) - (ny:error source 0 number-sound-anon b t)) - (nyq:add2 a b)) - - -(defun sal-equal (a b) - (or (and (numberp a) (numberp b) (= a b)) - (and (consp a) (consp b) (sal-list-equal a b)) - (equal a b))) - -(defun not-sal-equal (a b) - (not (sal-equal a b))) - -(defun sal-list-about-equal (a b) - (let ((rslt t)) ;; set to false if any element not equal - (dolist (x a) - (if (sal-about-equal x (car b)) - t ;; continue comparing - (return (setf rslt nil))) ;; break out of loop - (setf b (cdr b))) - (and rslt (null b)))) ;; make sure no leftovers in b - -(setf *~=tolerance* 0.000001) - -(defun sal-about-equal (a b) - (or (and (numberp a) (numberp b) (< (abs (- a b)) *~=tolerance*)) - (and (consp a) (consp b) (sal-list-about-equal a b)) - (equal a b))) diff --git a/Release/nyquist/seq.lsp b/Release/nyquist/seq.lsp deleted file mode 100644 index d360256057e98a2ae65ae008424961bdf4a3926d..0000000000000000000000000000000000000000 --- a/Release/nyquist/seq.lsp +++ /dev/null @@ -1,336 +0,0 @@ -;; seq.lsp -- sequence control constructs for Nyquist - -;; get-srates -- this either returns the sample rate of a sound or a -;; vector of sample rates of a vector of sounds -;; -(defun get-srates (sounds) - (cond ((arrayp sounds) - (let ((result (make-array (length sounds)))) - (dotimes (i (length sounds)) - (setf (aref result i) (snd-srate (aref sounds i)))) - result)) - (t - (snd-srate sounds)))) - -; These are complex macros that implement sequences of various types. -; The complexity is due to the fact that a behavior within a sequence -; can reference the environment, e.g. (let ((p 60)) (seq (osc p) (osc p))) -; is an example where p must be in the environment of each member of -; the sequence. Since the execution of the sequence elements are delayed, -; the environment must be captured and then used later. In XLISP, the -; EVAL function does not execute in the current environment, so a special -; EVAL, EVALHOOK must be used to evaluate with an environment. Another -; feature of XLISP (see evalenv.lsp) is used to capture the environment -; when the seq is first evaluated, so that the environment can be used -; later. Finally, it is also necessary to save the current transformation -; environment until later. -; -; The SEQ implementation passes an environment through closures that -; are constructed to evaluate expressions. SEQREP is similar, but -; the loop variable must be incremented and tested. -; -; Other considerations are that SEQ can handle multi-channel sounds, but -; we don't know to call the snd_multiseq primitive until the first -; SEQ expression is evaluated. Also, there's no real "NIL" for the end -; of a sequence, so we need several special cases: (1) The sequences -; is empty at the top level, so return silence, (2) There is one -; expression, so just evaluate it, (3) there are 2 expressions, so -; return the first followed by the second, (4) there are more than -; 2 expressions, so return the first followed by what is effectively -; a SEQ consisting of the remaining expressions. - - -;; SEQ-EXPR-EXPAND - helper function, expands expression to push/pop entry -;; on *sal-call-stack* to help debug calls into SAL from lazy evaluation -;; of SAL code by SEQ -(defun seq-expr-expand (expr source) - (if *sal-call-stack* - `(prog2 (sal-trace-enter '(,(strcat "Expression in " source ":") ,expr)) - ,expr ;; here is where the seq behavior is evaluated - (sal-trace-exit)) - expr)) - - -(defun with%environment (env expr) - ;; (progv (var1 ...) (val1 ...) expression-list) - `(progv ',*environment-variables* ,env ,expr)) -;(trace with%environment seq-expr-expand) - -(defmacro eval-seq-behavior (beh source) - ;(tracemacro 'eval-seq-behavior (list beh source) - (seq-expr-expand (with%environment 'nyq%environment - `(at-abs t0 - (force-srates s%rate ,beh))) source));) - -;; Previous implementations grabbed the environment and passed it from -;; closure to closure so that each behavior in the sequence could be -;; evaluated in the saved environment using an evalhook trick. This -;; version precomputes closures, which avoids using evalhook to get or -;; use the environment. It's still tricky, because each behavior has -;; to pass to snd-seq a closure that computes the remaining behavior -;; sequence. To do this, I use a recursive macro to run down the -;; behavior sequence, then as the recursion unwinds, construct nested -;; closures that all capture the current environment. We end up with a -;; closure we can apply to the current time to get a sound to return. -;; -(defmacro seq (&rest behlist) - ;; if we have no behaviors, return zero - (cond ((null behlist) - '(snd-zero (local-to-global 0) *sound-srate*)) - (t ; we have behaviors. Must evaluate one to see if it is multichan: - `(let* ((first%sound ,(seq-expr-expand (car behlist) "SEQ")) - (s%rate (get-srates first%sound)) - (nyq%environment (nyq:the-environment))) - ; if there's just one behavior, we have it and we're done: - ,(progn (setf behlist (cdr behlist)) - (if (null behlist) 'first%sound - ; otherwise, start the recursive construction: - `(if (arrayp first%sound) - (seq2-deferred snd-multiseq ,behlist) - (seq2-deferred snd-seq ,behlist)))))))) - - -;; seq2-deferred uses seq2 and seq3 to construct nested closures for -;; snd-seq. It is deferred so that we can first (in seq) determine whether -;; this is a single- or multi-channel sound before recursively constructing -;; the closures, since we only want to do it for either snd-seq or -;; snd-multiseq, but not both. It simply calls seq2 to begin the expansion. -;; -(defmacro seq2-deferred (seq-prim behlist) - (seq2 seq-prim behlist)) - - -#| -;; for debugging, you can replace references to snd-seq with this -(defun snd-seq-trace (asound aclosure) - (princ "Evaluating SND-SEQ-TRACE instead of SND-SEQ...\n") - (format t " Sound argument is ~A\n" asound) - (princ " Closure argument is:\n") - (pprint (get-lambda-expression aclosure)) - (princ " Calling SND-SEQ ...\n") - (let ((s (snd-seq asound aclosure))) - (format t " SND-SEQ returned ~A\n" s) - s)) - -;; also for debugging, you can uncomment some tracemacro wrappers from -;; macro definitions. This function prints what the macro expands to -;; along with name and args (which you add by hand to the call): -(defun tracemacro (name args expr) - (format t "Entered ~A with args:\n" name) - (pprint args) - (format t "Returned from ~A with expression:\n" name) - (pprint expr) - expr) -|# - - -;; we have at least 2 behaviors so we need the top level call to be -;; a call to snd-multiseq or snd-seq. This macro constructs the call -;; and uses recursion with seq3 to construct the remaining closures. -;; -(defun seq2 (seq-prim behlist) - `(,seq-prim first%sound - (prog1 ,(seq3 seq-prim behlist) ; <- passed to seq-prim - ;; we need to remove first%sound from the closure - ;; to avoid accumulating samples due to an unnecessary - ;; reference: - (setf first%sound nil)))) - -;; construct a closure that evaluates to a sequence of behaviors. -;; behlist has at least one behavior in it. -;; -(defun seq3 (seq-prim behlist) - `(lambda (t0) - (setf first%sound (eval-seq-behavior ,(car behlist) "SEQ")) - ,(progn (setf behlist (cdr behlist)) - (if (null behlist) 'first%sound - (seq2 seq-prim behlist))))) - - -; we have to use the real loop variable name since it could be -; referred to by the sound expression, so we avoid name collisions -; by using % in all the macro variable names -; -(defmacro seqrep (loop-control snd-expr) - ;(tracemacro "SEQREP" (list loop-control snd-expr) - `(let ((,(car loop-control) 0) - (loop%count ,(cadr loop-control)) - (nyq%environment (nyq:the-environment)) - s%rate seqrep%closure) - ; note: s%rate will tell whether we want a single or multichannel - ; sound, and what the sample rates should be. - (cond ((not (integerp loop%count)) - (error "bad argument type" loop%count)) - ((< loop%count 1) - (snd-zero (local-to-global 0) *sound-srate*)) - ((= loop%count 1) - ,snd-expr) - (t ; more than 1 iterations - (setf loop%count (1- loop%count)) - (setf first%sound ,snd-expr) - (setf s%rate (get-srates first%sound)) - (setf nyq%environment (nyq:the-environment)) - (if (arrayp first%sound) - (seqrep2 snd-multiseq ,loop-control ,snd-expr) - (seqrep2 snd-seq ,loop-control ,snd-expr))))));) - - -(defmacro seqrep2 (seq-prim loop-control snd-expr) - ;(tracemacro "SEQREP2" (list seq-prim loop-control snd-expr) - `(progn (setf seqrep%closure - (lambda (t0) ,(seqrep-iterate seq-prim loop-control snd-expr))) - (,seq-prim (prog1 first%sound (setf first%sound nil)) - seqrep%closure)));) - - -(defun seqrep-iterate (seq-prim loop-control snd-expr) - (setf snd-expr `(eval-seq-behavior ,snd-expr "SEQREP")) - `(progn - (setf ,(car loop-control) (1+ ,(car loop-control))) ; incr. loop counter - (if (>= ,(car loop-control) loop%count) ; last iteration - ,snd-expr - (,seq-prim ,snd-expr seqrep%closure)))) - - -;; TRIGGER - sums instances of beh which are launched when input becomes -;; positive (> 0). New in 2021: input is resampled to *sound-srate*. -;; As before, beh sample rates must match, so now they must also be -;; *sound-srate*. This implementation uses eval-seq-behavior to create -;; a more helpful stack trace for SAL. -(defmacro trigger (input beh) - `(let* ((nyq%environment (nyq:the-environment)) - (s%rate *sound-srate*)) - (snd-trigger (force-srate *sound-srate* ,input) - #'(lambda (t0) (eval-seq-behavior ,beh "TRIGGER"))))) - - -;; EVENT-EXPRESSION -- the sound of the event -;; -(setfn event-expression caddr) - - -;; EVENT-HAS-ATTR -- test if event has attribute -;; -(defun event-has-attr (note attr) - (expr-has-attr (event-expression note))) - - -;; EXPR-SET-ATTR -- new expression with attribute = value -;; -(defun expr-set-attr (expr attr value) - (cons (car expr) (list-set-attr-value (cdr expr) attr value))) - -(defun list-set-attr-value (lis attr value) - (cond ((null lis) (list attr value)) - ((eq (car lis) attr) - (cons attr (cons value (cddr lis)))) - (t - (cons (car lis) - (cons (cadr lis) - (list-set-attr-value (cddr lis) attr value)))))) - - -;; EXPAND-AND-EVAL-EXPR -- evaluate a note, chord, or rest for timed-seq -;; -(defun expand-and-eval-expr (expr) - (let ((pitch (member :pitch expr))) - (cond ((and pitch (cdr pitch) (listp (cadr pitch))) - (setf pitch (cadr pitch)) - (simrep (i (length pitch)) - (eval (expr-set-attr expr :pitch (nth i pitch))))) - (t - (eval expr))))) - - -;; (timed-seq '((time1 stretch1 expr1) (time2 stretch2 expr2) ...)) -;; a timed-seq takes a list of events as shown above -;; it sums the behaviors, similar to -;; (sim (at time1 (stretch stretch1 expr1)) ...) -;; but the implementation avoids starting all expressions at once -;; -;; Notes: (1) the times must be in increasing order -;; (2) EVAL is used on each event, so events cannot refer to parameters -;; or local variables -;; -;; If score events are very closely spaced (< 1020 samples), the block -;; overlap can cause a ripple effect where to complete one block of the -;; output, you have to compute part of the next score event, but then -;; it in turn computes part of the next score event, and so on, until -;; the stack overflows (if you have 1000's of events). -;; -;; This is really a fundamental problem in Nyquist because blocks are -;; not aligned. To work around the problem (but not totally solve it) -;; scores are evaluated up to a length of 100. If there are more than -;; 100 score events, we form a balanced tree of adders so that maybe -;; we will end up with a lot of sound in memory, but at least the -;; stack will not overflow. Generally, we should not end up with more -;; than 100 times as many blocks as we would like, but since the -;; normal space required is O(1), we're still using constant space + -;; a small constant * log(score-length). -;; -(setf MAX-LINEAR-SCORE-LEN 100) -(defun timed-seq (score) - (must-be-valid-score "TIMED-SEQ" score) - (let ((len (length score)) - pair) - (cond ((< len MAX-LINEAR-SCORE-LEN) - (timed-seq-linear score)) - (t ;; split the score -- divide and conquer - (setf pair (score-split score (/ len 2))) - (sum (timed-seq (car pair)) (timed-seq (cdr pair))))))) - -;; score-split -- helper function: split score into two, with n elements -;; in the first part; returns a dotted pair -(defun score-split (score n) - ;; do the split without recursion to avoid stack overflow - ;; algorithm: modify the list destructively to get the first - ;; half. Copy it. Reassemble the list. - (let (pair last front back) - (setf last (nthcdr (1- n) score)) - (setf back (cdr last)) - (rplacd last nil) - (setf front (append score nil)) ; shallow copy - (rplacd last back) - (cons front back))) - - -;; TIMED-SEQ-LINEAR - check to insure that times are strictly increasing -;; and >= 0 and stretches are >= 0 -(defun timed-seq-linear (score) - (let ((start-time 0) error-msg rslt) - (dolist (event score) - (cond ((< (car event) start-time) - (error (format nil - "Out-of-order time in TIMED-SEQ: ~A, consider using SCORE-SORT" - event))) - ((< (cadr event) 0) - (error (format nil "Negative stretch factor in TIMED-SEQ: ~A" event))) - (t - (setf start-time (car event))))) - ;; remove rests (a rest has a :pitch attribute of nil) - (setf score (score-select score #'(lambda (tim dur evt) - (expr-get-attr evt :pitch t)))) - (cond ((and score (car score) - (eq (car (event-expression (car score))) 'score-begin-end)) - (setf score (cdr score)))) ; skip score-begin-end data - (cond ((null score) (s-rest 0)) - (t - (at (caar score) - (seqrep (i (length score)) - (progn - (cond (*sal-call-stack* - (sal-trace-enter (list "Score event:" (car score)) nil nil) - (setf *sal-line* 0))) - (setf rslt - (cond ((cdr score) - (prog1 - (set-logical-stop - (stretch (cadar score) - (expand-and-eval-expr (caddar score))) - (- (caadr score) (caar score))) - (setf score (cdr score)))) - (t - (stretch (cadar score) (expand-and-eval-expr - (caddar score)))))) - (if *sal-call-stack* (sal-trace-exit)) - rslt))))))) diff --git a/Release/nyquist/seqfnint.lsp b/Release/nyquist/seqfnint.lsp deleted file mode 100644 index 1f7b01bdee33ce33626bb32cf72c5bab583ab3fc..0000000000000000000000000000000000000000 --- a/Release/nyquist/seqfnint.lsp +++ /dev/null @@ -1,31 +0,0 @@ - - (setfn seq-tag first) - (setfn seq-time second) - (setfn seq-line third) - (setfn seq-channel fourth) - (defun seq-value1 (e) (nth 4 e)) - (setfn seq-pitch seq-value1) ; pitch of a note - (setfn seq-control seq-value1) ; control number of a control change - (setfn seq-program seq-value1) ; program number of a program change - (setfn seq-bend seq-value1) ; pitch bend amount - (setfn seq-touch seq-value1) ; aftertouch amount - (defun seq-value2 (e) (nth 5 e)) - (setfn seq-velocity seq-value2) ; velocity of a note - (setfn seq-value seq-value2) ; value of a control change - (defun seq-duration (e) (nth 6 e)) - - - (setf seq-done-tag 0) - - (setf seq-other-tag 1) - - (setf seq-note-tag 2) - - (setf seq-ctrl-tag 3) - - (setf seq-prgm-tag 4) - - (setf seq-touch-tag 5) - - (setf seq-bend-tag 6) - diff --git a/Release/nyquist/seqmidi.lsp b/Release/nyquist/seqmidi.lsp deleted file mode 100644 index bea71145daf1dd229d0f630ad727a2786fe76f1a..0000000000000000000000000000000000000000 --- a/Release/nyquist/seqmidi.lsp +++ /dev/null @@ -1,171 +0,0 @@ -;; seqmidi.lsp -- functions to use MIDI files in Nyquist -; -; example call: -; -; (seq-midi my-seq -; (note (chan pitch velocity) (= chan 2) (my-note pitch velocity)) -; (ctrl (chan control value) (...)) -; (bend (chan value) (...)) -; (touch (chan value) (...)) -; (prgm (chan value) (setf (aref my-prgm chan) value)) - -;; seq-midi - a macro to create a sequence of sounds based on midi file -; -; -(defmacro seq-midi (the-seq &rest cases) - (seq-midi-cases-syntax-check cases) - `(let (_the-event _next-time _the-seq _seq-midi-closure _nyq-environment - _the-seq _tag) - (setf _the-seq (seq-copy ,the-seq)) - (setf _nyq-environment (nyq:the-environment)) - (setf _seq-midi-closure #'(lambda (t0) - (format t "_seq_midi_closure: t0 = ~A~%" t0) ;DEBUG - (prog (_the-sound) -loop ; go forward until we find note to play (we may be there) - ; then go forward to find time of next note - (setf _the-event (seq-get _the-seq)) - ; (display "seq-midi" _the-event t0) - (setf _tag (seq-tag _the-event)) - (cond ((= _tag seq-ctrl-tag) - ,(make-ctrl-handler cases)) - ((= _tag seq-bend-tag) - ,(make-bend-handler cases)) - ((= _tag seq-touch-tag) - ,(make-touch-handler cases)) - ((= _tag seq-prgm-tag) - ,(make-prgm-handler cases)) - ((= _tag seq-done-tag) - ; (format t "_seq_midi_closure: seq-done") - (cond (_the-sound ; this is the last sound of sequence - ; (format t "returning _the-sound~%") - (return _the-sound)) - (t ; sequence is empty, return silence - ; (format t "returning snd-zero~%") - (return (snd-zero t0 *sound-srate*))))) - ((and (= _tag seq-note-tag) - ,(make-note-test cases)) - (cond (_the-sound ; we now have time of next note - ; (display "note" (seq-time _the-event)) - (setf _next-time (/ (seq-time _the-event) 1000.0)) - (go exit-loop)) - (t - (setf _the-sound ,(make-note-handler cases)))))) - (seq-next _the-seq) - (go loop) -exit-loop ; here, we know time of next note - (display "seq-midi" _next-time) ;DEBUG - (format t "seq-midi calling snd-seq\n") ;DEBUG - (return (snd-seq - (set-logical-stop-abs _the-sound - (local-to-global _next-time)) - _seq-midi-closure))))) - (display "calling closure" (get-lambda-expression _seq-midi-closure)) ; DEBUG - (funcall _seq-midi-closure (local-to-global 0)))) - - -(defun seq-midi-cases-syntax-check (cases &aux n) - (cond ((not (listp cases)) - (break "syntax error in" cases))) - (dolist (case cases) - (cond ((or (not (listp case)) - (not (member (car case) '(NOTE CTRL BEND TOUCH PRGM))) - (not (listp (cdr case))) - (not (listp (cadr case))) - (not (listp (cddr case))) - (not (listp (last (cddr case))))) - (break "syntax error in" case)) - ((/= (length (cadr case)) - (setf n (cdr (assoc (car case) - '((NOTE . 3) (CTRL . 3) (BEND . 2) - (TOUCH . 2) (PRGM . 2)))))) - (break (format nil "expecting ~A arguments in" n) case)) - ((and (eq (car case) 'NOTE) - (not (member (length (cddr case)) '(1 2)))) - (break - "note handler syntax is (NOTE (ch pitch vel) [filter] behavior)" - case))))) - - -(defun make-ctrl-handler (cases) - (let ((case (assoc 'ctrl cases))) - (cond (case - `(let ((,(caadr case) (seq-channel _the-event)) - (,(cadadr case) (seq-control _the-event)) - (,(caddar (cdr case)) (seq-value _the-event))) - ,@(cddr case))) - (t nil)))) - -(defun make-bend-handler (cases) - (let ((case (assoc 'bend cases))) - (cond (case - `(let ((,(caadr case) (seq-channel _the-event)) - (,(cadadr case) (seq-value _the-event))) - ,@(cddr case))) - (t nil)))) - -(defun make-touch-handler (cases) - (let ((case (assoc 'touch cases))) - (cond (case - `(let ((,(caadr case) (seq-channel _the-event)) - (,(cadadr case) (seq-value _the-event))) - ,@(cddr case))) - (t nil)))) - -(defun make-prgm-handler (cases) - (let ((case (assoc 'pgrm cases))) - (cond (case - `(let ((,(caadr case) (seq-channel _the-event)) - (,(cadadr case) (seq-value _the-event))) - ,@(cddr case))) - (t nil)))) - -(defun make-note-test (cases) - (let ((case (assoc 'note cases))) - (cond ((and case (cdddr case)) - (caddr case)) - (t t)))) - - -(defun make-note-handler (cases) - (let ((case (assoc 'note cases)) - behavior) - (cond ((and case (cdddr case)) - (setf behavior (cadddr case))) - (t - (setf behavior (caddr case)))) - `(with%environment _nyq-environment - (with-note-args ,(cadr case) _the-event ,behavior)))) - - -(defmacro with-note-args (note-args the-event note-behavior) - ; (display "with-note-args" the-event) - `(let ((,(car note-args) (seq-channel ,the-event)) - (,(cadr note-args) (seq-pitch ,the-event)) - (,(caddr note-args) (seq-velocity ,the-event))) - (at (/ (seq-time ,the-event) 1000.0) - (stretch (/ (seq-duration ,the-event) 1000.0) ,note-behavior)))) - - -;(defun seq-next-note-time (the-seq find-first-flag) -; (prog (event) -; (if find-first-flag nil (seq-next the-seq)) -;loop -; (setf event (seq-get the-seq)) -; (cond ((eq (seq-tag event) seq-done-tag) -; (return (if find-first-flag 0.0 nil))) -; ((eq (seq-tag event) seq-note-tag) -; (return (/ (seq-time event) 1000.0)))) -; (seq-next the-seq) -; (go loop))) -; - -;; for SAL we can't pass in lisp expressions as arguments, so -;; we pass in functions instead, using keyword parameters for -;; ctrl, bend, touch, and prgm. The note parameter is required. -;; -(defun seq-midi-sal (seq note &optional ctrl bend touch prgm) - (seq-midi seq (note (chan pitch vel) (funcall note chan pitch vel)) - (ctrl (chan num val) (if ctrl (funcall ctrl chan num val))) - (bend (chan val) (if bend (funcall bend chan val))) - (touch (chan val) (if touch (funcall touch chan val))) - (prgm (chan val) (if prgm (funcall prgm chan val))))) diff --git a/Release/nyquist/sliders.lsp b/Release/nyquist/sliders.lsp deleted file mode 100644 index 292e87c6e1a07457fc25e3e7bee61312317cc765..0000000000000000000000000000000000000000 --- a/Release/nyquist/sliders.lsp +++ /dev/null @@ -1,196 +0,0 @@ -;; sliders.lsp -- communicate with NyquistIDE to implement control panels -;; Roger B. Dannenberg -;; April 2015 - -;; (stop-on-zero s) -- a sound that returns 1 until s goes to zero, then -;; the sound terminates. If s comes from a slider and you multiply -;; a sound by (stop-on-zero s), you can interactively stop it -;; (make-slider-panel "name" color) -- sets panel name for the following -;; sliders -;; (make-slider "param" [initial [low high]]) -- create slider named -;; "param" with optional range and initial value. Also returns -;; a sound. -;; (make-button "param" normal) -- create a button named "param" with -;; a starting value of normal (either 0 or 1). While the button -;; in the panel is pressed, the value changes to 1 or 0. -;; (get-slider-value "param") -- when called with a string, this looks up -;; the slider value by name -;; (slider-panel-close "name") -- close the panel window. Values of any -;; existing sliders become undefined. -;; (slider "panel" "name" [dur]) -- make a signal from slider value -;; (slider "name" [dur]) -- make a signal from slider in current panel -;; (get-slider-value "panel" "name") -- get a float value -;; (get-slider-value "name") -- get a float in current panel - -;; *active-slider-panel* is the current panel to which sliders are added -;; -(if (not (boundp '*active-slider-panel*)) - (setf *active-slider-panel* nil)) - -;; *panels-in-use* is an assoc list of panels, where each panel -;; is a list of allocated sliders stored as (name number) -;; -(if (not (boundp '*panels-in-use*)) - (setf *panels-in-use* nil)) - -;; allocate-slider-num -- find an unused slider number -;; linear search is used to avoid maintaining a parallel structure -;; for faster searching. We search starting at slider #10, leaving -;; sliders 0-9 unused; for example, you might want to control them -;; via open sound control, so this gives you 10 sliders that are -;; off limits to allocation by the SLIDER function. -;; -;; This code takes advantage of the fact that dotimes and dolist -;; return nil when they end normally, so we signal that we found -;; or did not find i by explicitly returning. Note that RETURN -;; returns from the innermost dotimes or dolist -- they do not -;; return from allocate-slider-num. -;; -(defun allocate-slider-num () - (dotimes (n 990) - (let ((i (+ n 10))) - (cond ((not (dolist (panel *panels-in-use*) - (cond ((dolist (pair (cdr panel)) - (cond ((eql (second pair) i) (return t)))) - (return t))))) - (return i)))))) - -;; remove panel from list of panels -(defun slider-panel-free (panel) - (setf *panels-in-use* (remove panel *panels-in-use* :test #'equal))) - -(setfn stop-on-zero snd-stoponzero) - -(defun make-slider-panel (name &optional (color 0)) - (let ((panel (assoc name *panels-in-use* :test #'equal))) - ;; first find if panel already exists. If so, free the resources - (cond (panel - (slider-panel-free panel))) - (setf *active-slider-panel* (list name)) - (setf *panels-in-use* (cons *active-slider-panel* *panels-in-use*)) - (format t "slider-panel-create: \"~A\" ~A~%" name color))) - -(defun make-slider (name &optional (init 0) (low 0) (high 1)) - (let ((num (allocate-slider-num))) - (cond ((null num) - (format t "WARNING: MAKE-SLIDER is out of slider numbers. ~A~%" - "No slider created.")) - ((not (and (stringp name) (numberp init) - (numberp low) (numberp high))) - (display - "WARNING: MAKE-SLIDER called with bad arguments. No slider created" - name init low high))) - ;; make sure we have an active panel - (cond ((null *active-slider-panel*) - (make-slider-panel "Controls"))) - ;; insert new slider into list of sliders in active panel. This - ;; is aliased with an element in the assoc list *panels-in-use*. - (rplacd *active-slider-panel* (cons (list name num) - (cdr *active-slider-panel*))) - (format t "slider-create: \"~A\" ~A ~A ~A ~A~%" name num init low high) - num)) - -(defun make-button (name &optional (normal 0)) - (let ((num (allocate-slider-num))) - (cond ((null num) - (format t "WARNING: MAKE-BUTTON is out of slider numbers. ~A~%" - "No button created.")) - ((not (and (stringp name) (numberp normal))) - (display - "WARNING: MAKE-BUTTON called with bad arguments. No button created" - name normal))) - ;; make sure we have an active panel - (cond ((null *active-slider-panel*) - (slider-panel "Controls"))) - ;; insert new button into list of controls in active panel. This - ;; is aliased with an element in the assoc list *panels-in-use*. - (rplacd *active-slider-panel* (cons (list name num) - (cdr *active-slider-panel*))) - (format t "button-create: \"~A\" ~A ~A~%" name num normal) - num)) - -(defun close-slider-panel (name) - (let ((panel (assoc name *panels-in-use* :test #'equal))) - (cond ((not (stringp name)) - (display "WARNING: SLIDER-PANEL-CLOSED called with bad argument." - name))) - (cond (panel - (slider-panel-free panel) - (format t "slider-panel-close: \"~A\"~%" name)) - (t - (format t "WARNING: slider panel ~A not found.~%" name))))) - -;; SLIDER-LOOKUP - find the slider by name -;; -(defun slider-lookup (name slider) - (let ((panel (assoc name *panels-in-use* :test #'equal)) s) - (cond ((null panel) - (error "Could not find slider panel named" name))) - (setf s (assoc slider (cdr panel) :test #'equal)) - (cond ((null s) - (error "Could not find slider named" s))) - (second s))) - - -;; SLIDER - creates a signal from real-time slider input -;; -;; options are: -;; (SLIDER number [dur]) -;; (SLIDER "name" [dur]) -- look up slider in current slider panel -;; (SLIDER "panel" "name" [dur]) -- look up panel, then look up slider -;; -(defun slider (id &optional slider-name dur) - (cond ((and (numberp id) (null slider-name)) - (setf dur 1.0)) - ((and (numberp id) (numberp slider-name) (null dur)) - (setf dur slider-name)) - ((and (stringp id) (null slider-name)) - (setf dur 1.0) - (setf id (slider-lookup (car *active-slider-panel*) id))) - ((and (stringp id) (numberp slider-name) (null dur)) - (setf dur slider-name) - (setf id (slider-lookup (car *active-slider-panel*) id))) - ((and (stringp id) (stringp slider-name) (null dur)) - (setf dur 1.0) - (setf id (slider-lookup id slider-name))) - ((and (stringp id) (stringp slider-name) (numberp dur)) - (setf id (slider-lookup id slider-name))) - (t - (error "SLIDER called with invalid arguments"))) - (setf dur (get-duration dur)) - (setf id (round id)) ;; just to make sure it's an integer - (cond ((or (< id 0) (>= id 1000)) - (error "SLIDER index out of bounds" id))) - (display "slider" id slider-name dur) - (snd-slider id *rslt* *sound-srate* dur)) - - -(if (not (boundp '*lpslider-cutoff*)) - (setf *lpslider-cutoff* 20.0)) - -(defun lpslider (id &optional slider-name dur) - (lp (slider id slider-name dur) 20.0)) - -;; save built-in get-slider-value so we can redefine it -(if (not (fboundp 'prim-get-slider-value)) - (setfn prim-get-slider-value get-slider-value)) - -(defun get-slider-value (id &optional slider-name) - (cond ((and (numberp id) (null slider-name)) nil) - ((and (stringp id) (null slider-name)) - (setf id (slider-lookup (car *active-slider-pael*) id))) - ((and (stringp id) (stringp slider-name)) - (setf id (slider-lookup id slider-name))) - (t - (error "GET-SLIDER-VALUE called with invalid arguments"))) - ;; further parameter checking is done in get-slider-value: - (prim-get-slider-value id)) - -(autonorm-off) -(snd-set-latency 0.02) -(print "**********sliders.lsp************************") -(print "WARNING: AUTONORM IS NOW TURNED OFF") -(print "WARNING: AUDIO LATENCY SET TO 20MS") -(print "To restore settings, execute (autonorm-on) and") -(print " (set-audio-latency 0.3)") -(print "*********************************************") diff --git a/Release/nyquist/sndfnint.lsp b/Release/nyquist/sndfnint.lsp deleted file mode 100644 index 015191b241c368069aaee644c3f52acb095f5672..0000000000000000000000000000000000000000 --- a/Release/nyquist/sndfnint.lsp +++ /dev/null @@ -1,92 +0,0 @@ - (SETF MAX-STOP-TIME 10E20) - - (SETF MIN-START-TIME -10E20) - - (setf OP-AVERAGE 1) (setf OP-PEAK 2) - - (setf snd-head-none 0) - - (setf snd-head-AIFF 1) - - (setf snd-head-IRCAM 2) - - (setf snd-head-NeXT 3) - - (setf snd-head-Wave 4) - - (setf snd-head-PAF 5) - - (setf snd-head-SVX 6) - - (setf snd-head-NIST 7) - - (setf snd-head-VOC 8) - - (setf snd-head-W64 9) - - (setf snd-head-MAT4 10) - - (setf snd-head-MAT5 11) - - (setf snd-head-PVF 12) - - (setf snd-head-XI 13) - - (setf snd-head-HTK 14) - - (setf snd-head-SDS 15) - - (setf snd-head-AVR 16) - - (setf snd-head-SD2 17) - - (setf snd-head-FLAC 18) - - (setf snd-head-CAF 19) - - (setf snd-head-raw 20) - - (setf snd-head-OGG 21) - - (setf snd-head-WAVEX 22) - - (setf snd-head-channels 1) - - (setf snd-head-mode 2) - - (setf snd-head-bits 4) - - (setf snd-head-srate 8) - - (setf snd-head-dur 16) - - (setf snd-head-latency 32) - - (setf snd-head-type 64) - - (setf snd-mode-adpcm 0) - - (setf snd-mode-pcm 1) - - (setf snd-mode-ulaw 2) - - (setf snd-mode-alaw 3) - - (setf snd-mode-float 4) - - (setf snd-mode-upcm 5) - - (setf snd-mode-unknown 6) - - (setf snd-mode-double 7) - - (setf snd-mode-GSM610 8) - - (setf snd-mode-DWVW 9) - - (setf snd-mode-DPCM 10) - - (setf snd-mode-msadpcm 11) - - (setf snd-mode-vorbis 11) - diff --git a/Release/nyquist/spec-plot.lsp b/Release/nyquist/spec-plot.lsp deleted file mode 100644 index a7651fbbb9da77bb5e5fce9aedd93278cf118ab5..0000000000000000000000000000000000000000 --- a/Release/nyquist/spec-plot.lsp +++ /dev/null @@ -1,47 +0,0 @@ -;; spec-plot.lsp -- spectral plot function -;; -;; Roger B. Dannenberg, May 2016 -;; - -(setf *spec-plot-bw* 8000.0) ;; highest frequency to plot (default) -(setf *spec-plot-res* 20.0) ;; bin size (default) -(setf *spec-plot-db* nil) ;; plot dB? (default) - -;; We want to allow round-number bin-sizes so plot will be more readable -;; Assuming 20Hz as an example, the FFT size would have to be -;; 44100/20 = 2205, but that's not a power of 2, so we should resample -;; the signal down so that the FFT size is 2048 (or up to 4096). This -;; would result in sample rates of 2048*20 = 40960 or 81120. We should -;; pick the smaller one if it is at least 2x *spec-plot-bw*. - -(defun spec-plot (sound &optional offset &key (res *spec-plot-res*) - (bw *spec-plot-bw*) - (db *spec-plot-db*)) - (ny:typecheck (not (soundp sound)) - (ny:error "SPEC-PLOT" 1 '((SOUND) nil) sound)) - (ny:typecheck (not (or (null offset) (numberp offset))) - (ny:error "SPEC-PLOT" 2 '((NUMBER NULL) nil) offset)) - (let (newsr sa fft-size power2) - (setf fft-size (/ (snd-srate sound) res)) - (setf power2 8) ;; find integer size for FFT - (while (< power2 fft-size) - (setf power2 (* 2 power2))) - ;; now power2 >= fft-size - (cond ((> power2 fft-size) ;; not equal, must resample - ;; if half power2 * res is above 2 * bw, - ;; use half power2 as fft size - (cond ((> (* power2 res) (* 4 bw)) - (setf power2 (/ power2 2)))) - (setf sound (snd-resample sound (* power2 res))) - (setf fft-size power2))) - ;; we only need fft-dur samples, but allow an extra second just to - ;; avoid any rounding errors - (if offset - (setf sound (extract offset (+ 1.0 offset (/ (snd-srate sound) - fft-size)) sound))) - (setf sa (sa-init :resolution res :input sound)) - (setf mag (sa-magnitude (sa-next sa))) - (setf mag (snd-from-array 0 (/ 1.0 res) mag)) - (if db (setf mag (linear-to-db mag))) - (s-plot mag bw (round (/ (float bw) res))))) - diff --git a/Release/nyquist/spectral-analysis.lsp b/Release/nyquist/spectral-analysis.lsp deleted file mode 100644 index 38ff748a0bcd0099c1b8dd753d56cf06884a15c3..0000000000000000000000000000000000000000 --- a/Release/nyquist/spectral-analysis.lsp +++ /dev/null @@ -1,289 +0,0 @@ -;; spectral-analysis.lsp -- functions to simplify computing -;; spectrogram data -;; -;; Roger B. Dannenberg and Gus Xia -;; Jan 2013, modified Oct 2017 - -;; API: -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; set sa-obj = sa-init(resolution: <nil or Hz>, -;; fft-dur: <nil or seconds>, -;; skip-period: <seconds>, -;; window: <window type>, -;; input: <filename or sound>) -;; -;; sa-init() creates a spectral-analysis object that can be used -;; to obtain spectral data from a sound. -;; -;; resolution is the width of each spectral bin in Hz. If nil of -;; not specified, the resolution is computed from fft-dur. -;; The actual resolution will be finer than the specified -;; resolution because fft sizes are rounded to a power of 2. -;; fft-dur is the width of the FFT window in seconds. The actual -;; FFT size will be rounded up to the nearest power of two -;; in samples. If nil, fft-dur will be calculated from -;; resolution. If both fft-size and resolution are nil -;; or not specified, the default value of 1024 samples, -;; corresponding to a duration of 1024 / signal-sample-rate, -;; will be used. If both resolution and fft-dur are -;; specified, the resolution parameter will be ignored. -;; Note that fft-dur and resolution are reciprocals. -;; skip-period specifies the time interval in seconds between -;; successive spectra (FFT windows). Overlapping FFTs are -;; possible. The default value overlaps windows by 50%. -;; Non-overlapped and widely spaced windows that ignore -;; samples by skipping over them entirely are also acceptable. -;; window specifies the type of window. The default is raised -;; cosine (Hann or "Hanning") window. Options include -;; :hann, :hanning, :hamming, :none, nil, where :none and -;; nil mean a rectangular window. -;; input can be a string (which specifies a sound file to read) -;; or a Nyquist SOUND to be analyzed. -;; Return value is an XLISP object that can be called to obtain -;; parameters as well as a sequence of spectral frames. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; set sa-frame = sa-next(sa-obj) -;; -;; sa-next() fetches the next spectrum from sa-obj. -;; -;; sa-obj is a spectral-analysis object returned by sa-init(). -;; Return value is an array of FLONUMS representing the discrete -;; spectrum. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; exec sa-info(sa-obj) -;; -;; sa-info prints information about the spectral computation. -;; -;; sa-obj is a spectral-analysis object returned by sa-init(). -;; Return value is nil, but information is printed. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; set mag = sa-magnitude(frame) -;; -;; sa-magnitude computes the magnitude (amplitude) spectrum -;; from a frame returned by sa-frame. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; exec sa-plot(sa-obj, sa-frame) -;; -;; sa-plot plots the amplitude (magnitude) spectrum of sa-frame. -;; -;; sa-obj is used to determine the bin width of data in sa-frame. -;; -;; sa-frame is a spectral frame (array) returned by sa-next() -;; -;; Return value is nil, but a plot is generated and displayed. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; set hz = sa-get-bin-width(sa-obj) -;; set n = sa-get-fft-size(sa-obj) -;; set secs = sa-get-fft-dur(sa-obj) -;; set window = sa-get-fft-window(sa-obj) -;; set skip-period = sa-get-skip-period(sa-obj) -;; set m = sa-get-fft-skip-size(sa-obj) -;; set sr = sa-get-sample-rate(sa-obj) -;; -;; These functions retrieve data from the sa-obj created by -;; sa-init. The return values are: -;; hz - the width of a frequency bin (also the separation -;; of bin center frequencies). The center frequency of -;; the i'th bin is i * hz. -;; n - the size of the FFT, an integer, a power of two. The -;; size of a spectral frame (an array returned by sa-next) -;; is (n / 2) + 1. -;; secs - the duration of an FFT window. -;; window - the type of window used (:hann, :hamming, :none) -;; skip-period - the time in seconds of the skip (the time -;; difference between successive frames -;; m - the size of the skip in samples. -;; sr - the sample rate of the sound being analyzed (in Hz, a flonum) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;; define the class of spectral analysis objects -(setf sa-class (send class :new '(sound length skip window window-type))) - -(send sa-class :answer :next '() '( - (snd-fft sound length skip window))) - -(defun sa-raised-cosine (alpha beta) - (sum (const alpha) - (scale beta (lfo 1.0 1.0 *sine-table* 270)))) - -(defun sa-fft-window (frame-size alpha beta) - (abs-env (control-srate-abs frame-size - (sa-raised-cosine alpha beta)))) - -(defun hann-window (frame-size) (sa-fft-window frame-size 0.5 0.5)) -(defun hamming-window (frame-size) (sa-fft-window frame-size 0.54 0.46)) - -(defun sa-get-window-type (win-type) - (case win-type - ((:hann :hanning) :hann) - ((nil :none) :none) - (:hamming :hamming) - (t (print "Warning: invalid window-type parameter: ~A~%" win-type) - (print " Using :HAMMING instead.~%") - :hamming))) - - -(defun sa-compute-window (len win-type) - (case win-type - (:hann (hann-window len)) - (:none nil) - (:hamming (hamming-window len)) - (t (print "Warning: invalid window-type parameter: ~A~%" win-type) - (print " Using :HAMMING instead.~%") - (hamming-window len)))) - - -(send sa-class :answer :isnew '(snd len skp win-type) '( - (setf sound snd) - (setf length len) - (setf skip skp) - (setf window-type (sa-get-window-type win-type)) - (setf window (sa-compute-window length window-type)))) - - -;; sa-to-mono -- sum up the channels in an array -;; -(defun sa-to-mono (s) - (let ((mono (aref s 0))) - (dotimes (i (1- (length s))) - (setf mono (sum mono (aref s (1+ i))))) - mono)) - - -(defun sa-init (&key resolution fft-dur skip-period window input) - (let (len sr n skip) - (cond ((stringp input) - (setf input (s-read input)))) - (cond ((arrayp input) - (format t "Warning: sa-init is converting stereo sound to mono~%") - (setf input (sa-to-mono input))) - ((soundp input) ;; so that variables are not "consumed" by snd-fft - (setf input (snd-copy input)))) - (cond ((not (soundp input)) - (error - (format nil - "Error: sa-init did not get a valid :input parameter~%")))) - (setf sr (snd-srate input)) - (setf len 1024) - (cond (fft-dur - (setf len (* fft-dur sr))) - (resolution - (setf len (/ sr resolution)))) - ;; limit fft size to between 4 and 2^16 - (cond ((> len 65536) - (format t "Warning: fft-size reduced from ~A to 65536~%" len) - (setf len 65536)) - ((< len 4) - (format t "Warning: fft-size increased from ~A to 4~%" len) - (setf len 4))) - ;; round up len to a power of two - (setf n 4) - (while (< n len) - (setf n (* n 2))) - (setf length n) ;; len is now an integer power of 2 - ;(display "sa-init" length) - ;; compute skip length - default is len/2 - (setf skip (if skip-period (round (* skip-period sr)) - (/ length 2))) - (send sa-class :new input length skip window))) - - -(defun sa-next (sa-obj) - (send sa-obj :next)) - -(defun sa-info (sa-obj) - (send sa-obj :info)) - -(send sa-class :answer :info '() '( - (format t "Spectral Analysis object (instance of sa-class):~%") - (format t " resolution (bin width): ~A Hz~%" (/ (snd-srate sound) length)) - (format t " fft-dur: ~A s (~A samples)~%" (/ length (snd-srate sound)) length) - (format t " skip-period: ~A s (~A samples)~%" (/ skip (snd-srate sound)) skip) - (format t " window: ~A~%" window-type) - nil)) - - -(defun sa-plot (sa-obj frame) - (send sa-obj :plot frame)) - -(defun sa-magnitude(frame) - (let* ((flen (length frame)) - (n (/ (length frame) 2)) ; size of amplitude spectrum - 1 - (as (make-array (1+ n)))) ; amplitude spectrum - ;; first compute an amplitude spectrum - (setf (aref as 0) (abs (aref frame 0))) ;; DC - ;; half_n is actually length/2 - 1, the number of complex pairs - ;; in addition there is the DC and Nyquist terms, which are - ;; real and in the first and last slots of frame - (setf half_n (1- n)) - (dotimes (i half_n) - (let* ((i2 (+ i i 2)) ; index of the imag part - (i2m1 (1- i2)) ; index of the real part - (amp (sqrt (+ (* (aref frame i2m1) (aref frame i2m1)) - (* (aref frame i2) (aref frame i2)))))) - (setf (aref as (1+ i)) amp))) - (setf (aref as n) (aref frame (1- flen))) - as)) ;; return the amplitude spectrum - - -(send sa-class :answer :plot '(frame) '( - (let* ((as (sa-magnitude frame)) - (sr (snd-srate sound))) - (s-plot (snd-from-array 0 (/ length sr) as) - sr (length as))))) - -(defun sa-get-bin-width (sa-obj) - (send sa-obj :get-bin-width)) - -(send sa-class :answer :get-bin-width '() - '((/ (snd-srate sound) length))) - -(defun sa-get-fft-size (sa-obj) - (send sa-obj :get-fft-size)) - -(send sa-class :answer :get-fft-size '() '(length)) - -(defun sa-get-fft-dur (sa-obj) - (send sa-obj :get-fft-dur)) - -(send sa-class :answer :get-fft-dur '() '(/ length (snd-srate sound))) - -(defun sa-get-fft-window (sa-obj) - (send sa-obj :get-fft-window)) - -(send sa-class :answer :get-fft-window '() '(window-type)) - -(defun sa-get-fft-skip-period (sa-obj) - (send sa-obj :get-skip-period)) - -(send sa-class :answer :get-skip-period '() '((/ skip (snd-srate sound)))) - -(defun sa-get-fft-skip-size (sa-obj) - (send sa-obj :get-skip-size)) - -(send sa-class :answer :get-fft-skip-size '() '(skip)) - -(defun sa-get-sample-rate (sa-obj) - (send sa-obj :get-sample-rate)) - -(send sa-class :answer :get-sample-rate '() '((snd-srate sound))) - - -;;;;;;; TESTS ;;;;;;;;;; - - -(defun plot-test () - (let (frame) - (setf sa (sa-init :input "./rpd-cello.wav")) - (while t - (setf frame (sa-next sa)) - (if (null sa) (return nil)) - (sa-plot sa frame)))) - diff --git a/Release/nyquist/stk.lsp b/Release/nyquist/stk.lsp deleted file mode 100644 index 3eae1390851ba037c5ecbe8d6111dd9ee308ff34..0000000000000000000000000000000000000000 --- a/Release/nyquist/stk.lsp +++ /dev/null @@ -1,200 +0,0 @@ -;; stk.lsp -- STK-based instruments -;; -;; currently clarinet and saxophony are implemented - -(defun instr-parameter (parm) - ;; coerce parameter into a *sound-srate* signal - (cond ((numberp parm) - (stretch 30 (control-srate-abs *sound-srate* (const (float parm))))) - (t - (force-srate *sound-srate* parm)))) - - -(defun clarinet (step breath-env) - (snd-clarinet (step-to-hz step) (force-srate *sound-srate* breath-env) *sound-srate*)) - - -(defun clarinet-freq (step breath-env freq-env) - ;; note that the parameters are in a different order -- I defined - ;; clarinet-freq this way so that the first two parameters are always - ;; step and breath. I didn't redo snd-clarinet-freq. - (snd-clarinet_freq (step-to-hz step) - (instr-parameter breath-env) - (instr-parameter freq-env) - *sound-srate*)) - - - -(defun clarinet-all (step breath-env freq-env vibrato-freq vibrato-gain reed-stiffness noise) - ;; note that the parameters are not in the same order as snd-clarinet-all - (setf breath-env (instr-parameter breath-env)) - (setf freq-env (instr-parameter freq-env)) - (setf reed-stiffness (instr-parameter reed-stiffness)) - (setf noise (instr-parameter noise)) - (snd-clarinet_all (step-to-hz step) - breath-env freq-env - ;; STK scales 1.0 to 12Hz. Scale here so vibrato-freq is in Hz - (/ vibrato-freq 12.0) vibrato-gain - reed-stiffness noise - *sound-srate*)) - - -(defun sax (step breath-env) - (snd-sax (step-to-hz step) (force-srate *sound-srate* breath-env) *sound-srate*)) - -(defun sax-freq (step breath-env freq-env) - (snd-sax_freq (step-to-hz step) - (instr-parameter breath-env) - (instr-parameter freq-env) - *sound-srate*)) - -(defun sax-all (step breath-env freq-env vibrato-freq vibrato-gain reed-stiffness noise blow-pos reed-table-offset) - (snd-sax_all (step-to-hz step) - (instr-parameter freq-env) - (instr-parameter breath-env) - (instr-parameter (/ vibrato-freq 12.0)) - (instr-parameter vibrato-gain) - (instr-parameter reed-stiffness) - (instr-parameter noise) - (instr-parameter blow-pos) - (instr-parameter reed-table-offset) - *sound-srate*) -) - -; instr-parameter already defined in stk.lsp - -(defun flute (step breath-env) - (snd-flute (step-to-hz step) (force-srate *sound-srate* breath-env) *sound-srate*)) - -(defun flute-freq (step breath-env freq-env) - (snd-flute_freq (step-to-hz step) - (instr-parameter breath-env) - (instr-parameter freq-env) - *sound-srate*)) - -(defun flute-all (step breath-env freq-env vibrato-freq vibrato-gain jet-delay noise) - ;; note that the parameters are not in the same order as snd-clarinet-all - (setf breath-env (instr-parameter breath-env)) - (setf freq-env (instr-parameter freq-env)) - (setf jet-delay (instr-parameter jet-delay)) - (setf noise (instr-parameter noise)) - (snd-flute_all (step-to-hz step) - breath-env freq-env - ;; STK scales 1.0 to 12Hz. Scale here so vibrato-freq is in Hz - (/ vibrato-freq 12.0) vibrato-gain - jet-delay noise - *sound-srate*)) - - -(defun bowed (step bowpress-env) - (snd-bowed (step-to-hz step) (force-srate *sound-srate* bowpress-env) *sound-srate*)) - -(defun bowed-freq (step bowpress-env freq-env) - (snd-bowed_freq (step-to-hz step) - (instr-parameter bowpress-env) - (instr-parameter freq-env) - *sound-srate*)) - -(defun mandolin (step dur &optional (detune 4.0)) - (let ((d (get-duration dur))) - (snd-mandolin *rslt* (step-to-hz step) d 1.0 detune *sound-srate*))) - -(defun wg-uniform-bar (step bowpress-env) - (snd-bandedwg (step-to-hz step) (force-srate *sound-srate* bowpress-env) 0 *sound-srate*)) - -(defun wg-tuned-bar (step bowpress-env) - (snd-bandedwg (step-to-hz step) (force-srate *sound-srate* bowpress-env) 1 *sound-srate*)) - -(defun wg-glass-harm (step bowpress-env) - (snd-bandedwg (step-to-hz step) (force-srate *sound-srate* bowpress-env) 2 *sound-srate*)) - -(defun wg-tibetan-bowl (step bowpress-env) - (snd-bandedwg (step-to-hz step) (force-srate *sound-srate* bowpress-env) 3 *sound-srate*)) - -(defun modalbar (preset step duration) - (let ((preset (case preset - (MARIMBA 0) - (VIBRAPHONE 1) - (AGOGO 2) - (WOOD1 3) - (RESO 4) - (WOOD2 5) - (BEATS 6) - (TWO-FIXED 7) - (CLUMP 8) - (t (error (format nil "Unknown preset for modalbar %A" preset))))) - (d (get-duration duration))) - (snd-modalbar *rslt* (step-to-hz step) preset d *sound-srate*))) - -(defun sitar (step dur) - (let ((d (get-duration dur))) - (snd-sitar *rslt* (step-to-hz step) d *sound-srate*))) - -(defun nyq:nrev (snd rev-time mix) - (snd-stkrev 0 snd rev-time mix)) - -(defun nyq:jcrev (snd rev-time mix) - (snd-stkrev 1 snd rev-time mix)) - -(defun nyq:prcrev (snd rev-time mix) - (snd-stkrev 2 snd rev-time mix)) - -(defun nrev (snd rev-time mix) - (multichan-expand "NREV" #'nyq:nrev - '(((SOUND) "snd") ((NUMBER) "rev-time") ((NUMBER) "mix")) - snd rev-time mix)) - -(defun jcrev (snd rev-time mix) - (multichan-expand "JCREV" #'nyq:jcrev - '(((SOUND) "snd") ((NUMBER) "rev-time") ((NUMBER) "mix")) - snd rev-time mix)) - -(defun prcrev (snd rev-time mix) - (multichan-expand "PRCREV" #'nyq:prcrev - '(((SOUND) "snd") ((NUMBER) "rev-time") ((NUMBER) "mix")) - snd rev-time mix)) - -(defun nyq:chorus (snd depth freq mix &optional (base-delay 6000)) - (snd-stkchorus snd base-delay depth freq mix)) - -(defun stkchorus (snd depth freq mix &optional (base-delay 6000)) - (multichan-expand "STKCHORUS" #'nyq:chorus - '(((SOUND) "snd") ((NUMBER) "depth") ((NUMBER) "freq") ((NUMBER) "mix") - ((INTEGER) "base-delay")) - snd depth freq mix base-delay)) - -(defun nyq:pitshift (snd shift mix) - (snd-stkpitshift snd shift mix)) - -(defun pitshift (snd shift mix) - (multichan-expand "PITSHIFT" #'nyq:pitshift - '(((SOUND) "snd") ((NUMBER) "shift") ((NUMBER) "mix")) - snd shift mix)) - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; HELPER FUNCTIONS -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; pass in rates of increase/decrease in begin/end... this is like noteOn and noteOff -; -; STK uses setRate but the actual ramp time is also a function of the sample rate. -; I will assume the clarinet was run at 44100Hz and fix things so that the envelope -; is sample-rate independent. -; -; STK seemed to always give a very fast release, so I changed the numbers so that -; note-off values from 0.01 to 1 give an interesting range of articulations. -; -; IMPORTANT: the returned envelope is 0.1s longer than dur. There is 0.1s of silence -; at the end so that the clarinet can "ring" after the driving force is removed. -; -(defun stk-breath-env (dur note-on note-off) - (let* ((target (+ 0.55 (* 0.3 note-on))) - (on-time (/ (* target 0.0045) note-on)) - (off-time (/ (* target 0.02) note-off))) - ;(display "clarinet-breath-env" target on-time off-time) - (pwl on-time target - (- dur off-time) target - dur 0 (+ dur 0.1)))) - - diff --git a/Release/nyquist/system.lsp b/Release/nyquist/system.lsp deleted file mode 100644 index b750fe66693d86af2fe6d49e2423aad2fcefde25..0000000000000000000000000000000000000000 --- a/Release/nyquist/system.lsp +++ /dev/null @@ -1,131 +0,0 @@ -; machine.lsp -- machine/system-dependent definitions -; Windows - -;; default behavior is to call SETUP-CONSOLE to get large white typescript -;; -;; set *setup-console* to nil in your personal init.lsp to override this behavior -;; (this may be necessary to work with emacs) -;; -(if (not (boundp '*setup-console*)) (setf *setup-console* t)) -(if *setup-console* (setup-console)) - -(if (not (boundp '*default-sf-format*)) - (setf *default-sf-format* snd-head-Wave)) - -(if (not (boundp '*default-sound-file*)) - (compute-default-sound-file)) - -(if (not (boundp '*default-sf-dir*)) - (setf *default-sf-dir* "")) - -(if (not (boundp '*default-sf-mode*)) - (setf *default-sf-mode* snd-mode-pcm)) - -(if (not (boundp '*default-sf-bits*)) - (setf *default-sf-bits* 16)) - -(if (not (boundp '*default-plot-file*)) - (setf *default-plot-file* "points.dat")) - -;(if (not (boundp '*plotscript-file*)) -; (setf *plotscript-file* "sys/unix/rs6k/plotscript")) - -; local definition for play -(defmacro play (expr) - `(s-save-autonorm ,expr NY:ALL *default-sound-file* :play *soundenable*)) - - -(defun r () - (s-save (s-read *default-sound-file*) NY:ALL "" :play t) -) - - -; PLAY-FILE -- play a file -(defun play-file (name) - (s-save (s-read name) NY:ALL "" :play t)) - - -; FULL-NAME-P -- test if file name is a full path or relative path -; -; (otherwise the *default-sf-dir* will be prepended -; -(defun full-name-p (filename) - (or (eq (char filename 0) #\\) - (eq (char filename 0) #\/) - (eq (char filename 0) #\.) - (and (> (length filename) 2) - (both-case-p (char filename 0)) - (equal (char filename 1) #\:)))) - -; RELATIVE-PATH-P -- test if filename or path is a relative path -; -; note that properly converting a Windows path from relative to -; absolute is complicated by paths like: E:MYFILE.LSP -; Nyquist assumes that if there is a drive letter, the path is -; absolute, e.g. E:\TMP\MYFILE.LSP and if there is no drive, -; the path is relative, e.g. you cannot have \TMP\MYFILE.LSP -; -(defun relative-path-p (filename) - (or (< (length filename) 2) - (not (both-case-p (char filename 0))) - (not (equal (char filename 1) #\:)))) - - -(setf *file-separator* #\\) - -(defun ny:load-file () (load "*.*")) -(defun ny:reload-file () (load "*")) - - -; save the standard function to write points to a file -; -;(setfn s-plot-points s-plot) - -;(defun array-max-abs (points) -; (let ((m 0.0)) -; (dotimes (i (length points)) -; (setf m (max m (abs (aref points i))))) -; m)) - -;(setf graph-width 600) -;(setf graph-height 220) - -;(defun s-plot (snd &optional (n 600)) -; (show-graphics) -; (clear-graphics) -; (cond ((soundp snd) -; (s-plot-2 snd n (/ graph-height 2) graph-height)) -; (t -; (let ((gh (/ graph-height (length snd))) -; hs) -; (dotimes (i (length snd)) -; (setf hs (s-plot-2 (aref snd i) n (+ (/ gh 2) (* i gh)) gh hs))))))) -; -; -;(defun s-plot-2 (snd n y-offset graph-height horizontal-scale) -; (prog ((points (snd-samples snd n)) -; maxpoint horizontal-scale vertical-scale) -; (setf maxpoint (array-max-abs points)) -; (moveto 0 y-offset) -; (lineto graph-width y-offset) -; (moveto 0 y-offset) -; (cond ((null horizontal-scale) -; (setf horizontal-scale (/ (float graph-width) (length points))))) -; (setf vertical-scale (- (/ (float graph-height) 2 maxpoint))) -; (dotimes (i (length points)) -; (lineto (truncate (* horizontal-scale i)) -; (+ y-offset (truncate (* vertical-scale (aref points i)))))) -; (format t "X Axis: ~A to ~A (seconds)\n" (snd-t0 snd) (/ (length points) (snd-srate snd))) -; (format t "Y Axis: ~A to ~A\n" (- maxpoint) maxpoint) -; (format t "~A samples plotted.\n" (length points)) -; (return horizontal-scale) -; )) -; -; S-EDIT - run the audio editor on a sound -; -;(defmacro s-edit (&optional expr) -; `(prog () -; (if ,expr (s-save ,expr 1000000000 *default-sound-file*)) -; (system (format nil "audio_editor ~A &" -; (soundfilename *default-sound-file*))))) - diff --git a/Release/nyquist/test.lsp b/Release/nyquist/test.lsp deleted file mode 100644 index 3bacbc62aba7d5128fddc510c084cf2670472632..0000000000000000000000000000000000000000 --- a/Release/nyquist/test.lsp +++ /dev/null @@ -1,43 +0,0 @@ - -(defun ss () (osc c5)) - -(defun tt () (stretch 2 (snd-tapv (ss) 1.1 (scale *d* (lfo 10)) 2.2))) -(setf *d* .01) - -(defun g () (play (tt))) - -;(set-sound-srate 10) -;(set-control-srate 10) -(defun rr () (stretch 10 (ramp))) -(defun ll () (stretch 10 (lfo .5))) -(defun xx () (snd-tapv (rr) 1.1 (ll) 2.2)) -(defun h () (snd-samples (xx) 150)) - -(defun chorus (sound maxdepth depth rate saturation) - (let ((modulation (prod depth (stretch-abs 10000.0 (general-lfo rate)))) - (offset (/ maxdepth 2.0)) - chor) - (setf chor (snd-tapv sound offset modulation maxdepth)) - (sum (prod chor saturation) (prod (seq (s-rest offset) sound) - (sum 1.0 (prod -1.0 saturation)))))) - - -(set-sound-srate 22050.0) - -(defun f () - (chorus (s-read "runtime\\ah.wav") .1 .1 1 .5)) - -(defun e () - (seq (s-rest .05) (chorus (s-read "rpd.wav") .07 .07 .7 .5))) - -(defun d () (sum (e) (f))) - -(defun rou () (s-read "round.wav" :time-offset 1.18 :dur (- 8.378 1.18))) - -(defun rou4 () (sim (rou) - (at *rd* (rou)) - (at (* *rd* 2) (rou)) - (at (* *rd* 3) (rou)))) - - - diff --git a/Release/nyquist/velocity.lsp b/Release/nyquist/velocity.lsp deleted file mode 100644 index aa5226d638d2d92f8a45f7ab595e3e56fce9c5b5..0000000000000000000000000000000000000000 --- a/Release/nyquist/velocity.lsp +++ /dev/null @@ -1,24 +0,0 @@ -;; velocity.lsp -- conversion routines for MIDI velocity -;; -;; Roger B. Dannenberg -;; July, 2012 - - -(defun db-to-vel (x &optional float) - (linear-to-vel (db-to-linear x) float)) - - -(defun linear-to-vel (x &optional float) - (setf x (/ (- (sqrt (abs x)) 0.0239372) 0.00768553)) - (cond (float x) - (t - (setf x (round x)) - (max 1 (min 127 x))))) - - -(defun vel-to-db (v) - (linear-to-db (vel-to-linear v))) - - -(defun vel-to-linear (v) - (power (+ (* v 0.00768553) 0.0239372) 2)) diff --git a/Release/nyquist/xlinit.lsp b/Release/nyquist/xlinit.lsp deleted file mode 100644 index ae2cfda222d686d1ab6c9af64924d69fae5e883d..0000000000000000000000000000000000000000 --- a/Release/nyquist/xlinit.lsp +++ /dev/null @@ -1,67 +0,0 @@ -;; xlinit.lsp -- standard definitions and setup code for XLisp -;; - - -(defun bt () (baktrace 6)) - -(defmacro setfn (a b) - `(setf (symbol-function ',a) (symbol-function ',b))) - -(setfn co continue) -(setfn top top-level) -(setfn res clean-up) -(setfn up clean-up) - -;## display -- debugging print macro -; -; call like this (display "heading" var1 var2 ...) -; and get printout like this: -; "heading : VAR1 = <value> VAR2 = <value> ...<CR>" -; -; returns: -; (let () -; (format t "~A: " ,label) -; (format t "~A = ~A " ',item1 ,item1) -; (format t "~A = ~A " ',item2 ,item2) -; ...) -; -(defmacro display-macro (label &rest items) - (let ($res$) - (dolist ($item$ items) - (setq $res$ (cons - `(format t "~A = ~A " ',$item$ ,$item$) - $res$))) - (append (list 'let nil `(format t "~A : " ,label)) - (reverse $res$) - '((terpri))))) - - -(defun display-on () (setfn display display-macro) t) -(defun display-off () (setfn display or) nil) -(display-on) - -; (objectp expr) - object predicate -; -;this is built-in: (defun objectp (x) (eq (type-of x) 'OBJ)) - - -; (filep expr) - file predicate -; -(defun filep (x) (eq (type-of x) 'FPTR)) - -(load "profile.lsp" :verbose NIL) - -; (setf *breakenable* t) -- good idea, but set it in init.lsp, so user can decide -(setq *tracenable* nil) - -(defmacro defclass (name super locals class-vars) - (if (not (boundp name)) - (if super - `(setq ,name (send class :new ',locals ',class-vars ,super)) - `(setq ,name (send class :new ',locals ',class-vars))))) - -;(cond ((boundp 'application-file-name) -; (load application-file-name))) - -(setq *gc-flag* t) - diff --git a/Release/nyquist/xm.lsp b/Release/nyquist/xm.lsp deleted file mode 100644 index 75bdea2c1bed61087aaa3159f65e3a63be9cf60b..0000000000000000000000000000000000000000 --- a/Release/nyquist/xm.lsp +++ /dev/null @@ -1,2767 +0,0 @@ -;; X-Music, inspired by Commmon Music - -#| -PATTERN SEMANTICS - -Patterns are objects that are generally accessed by calling (next -pattern). Each call returns the next item in an infinite sequence -generated by the pattern. Items are organized into periods. You can -access all (remaining) items in the current period using (next pattern -t). - -Patterns mark the end-of-period with +eop+, a distinguished atom. The -+eop+ markers are filtered out by the next() function but returned by -the :next method. - -Pattern items may be patterns. This is called a nested pattern. When -patterns are nested, you return a period from the innermost pattern, -i.e. traversal is depth-first. This means when you are using something -like random, you select a random pattern and get an item from it. The -next time you handle :next, you get another item from the same pattern -until the pattern returns +eonp+, which you can read as "end of nested -pattern". Random would then advance to the next random pattern and get -an item from it. - -While generating from a nested pattern, you might return many periods -including +eop+, but you do not advance to the next pattern at any -given level until that level receives +eonp+ from the next level down. - -With nested patterns, i.e. patterns with items that are patterns, the -implementation requires that *all* items must be patterns. The -application does *not* have to make every item a pattern, so the -implementation "cleans up" the item list: Any item that is not a -pattern is be replaced with a cycle pattern whose list contains just -the one item. - -PATTERN LENGTH - -There are two sorts of cycles and lengths. The nominal pattern -behavior, illustrated by cycle patterns, is to cycle through a -list. There is a "natural" length computed by :start-period and stored -in count that keeps track of this. - -The second cycle and length is established by the :for parameter, -which is optional. If a number or pattern is provided, it controls the -period length and overrides any default periods. When :for is given, -count is set and used as a counter to count the items remaining in -a period. - -To summarize, there are 3 ways to determine lengths: - -1) The length is implicit. The length can be computed by :start-period -and turned into an explicit length stored in count. - -2) The length is explicitly set with :for. This overrides the implicit -length. The explicit length is stored as count that tells how many -more items to generate in the current period. - -3) The length can be generated by a pattern. The pattern is evaluated -in :start-period to generate an explicit length. - -In case (1), a pattern object does not return +eonp+ to the next level -up unless it receives an +eonp+ from one level down *and* is at the -end of its period. E.g. in the random pattern, if there are three -nested patterns, random must see +eonp+ three times and make three -random pattern selections before returning +eonp+ to the next level -up. This is the basic mechanism for achieving a "depth-first" -expansion of patterns. - -However, there is still the question of periods. When a nested pattern -returns a period, do the containing pattern return that period or -merge the period with other periods from other nested patterns? The -default is to return periods as they are generated by sub-patterns. In -other words, when a nested pattern returns +eop+ (end of period), that -token is returned by the :next message. Thus, in one "natural" cycle -of a pattern of patterns, there can be many periods (+eop+'s) before -+eonp+ is returned, marking the end of the "natural" pattern at this -level. - -The alternative strategy, which is to filter out all the +eop+'s and -form one giant pattern that runs up to the natural length (+eonp+) for -this level, can be selected by setting the :merge parameter to true. -Note that :merge is ignored if :for is specified because :for says -exactly how many items are in each period. - -The Copier pattern is an interesting case. It's :start-pattern should -get the next period from its sub-pattern, a repeat count from the -:repeat pattern, and a boolean from the :merge pattern. Then, it -should make that many copies, returning them as multiple periods or as -one merged one, depending on :merge, followed by +eonp+, after which -:start-pattern is called and the process repeats. But if :for 10 is -provided, this means we need to return a single period of 10 items. We -call :start-pattern, then repeat the sub-pattern's period until we -have 10 items. Thus, we ignore the :merge flag and :repeat count. -This makes Copier with a :for parameter equivalent to Cycle with a -single sub-pattern in a list. If you think :for should not override -these parameters (:repeat and :merge), you can probably get what you -want by using a Length pattern to regroup the output of a Copier. - -IMPLEMENTATION - -Most pattern behavior is implemented in a few inherited methods. - -:next gets the next item or period. If there is a length-pattern -(from :for), :next groups items into periods, filtering out +eop+ and -+eonp+. If there is no length-pattern, :next passes +eop+ through and -watches for +eonp+ to cause the pattern to re-evaluate pattern -parameters. - -Several methods are implemented by subclasses of pattern-class: - -:START-PERIOD is called before the first advance and before the first -item of a period controlled by :for. It sets count to the "natural" -length of the period. HAVE-CURRENT will be set to false. - -:ADVANCE advances to the next item in the pattern. If there are nested -patterns, advance is called to select the first nested pattern, then -items are returned until +eonp+ is seen, then we advance to the next -pattern, etc. After :ADVANCE, HAVE-CURRENT is true. - -CURRENT is set by advance to the current item. If this has nested -patterns, current is set to a pattern, and the pattern stays there in -current until advance is called, either at the end of period or when -+eonp+ is seen. - -HAVE-CURRENT is a boolean to tell when CURRENT is valid. - -IS-NESTED - set when there are nested patterns. If there are, make all -items of any nested pattern be patterns (no mix of patterns and -non-patterns is allowed; use - (MAKE-CYCLE (LIST item)) -to convert a non-pattern to a pattern). - -Patterns may be shared, so the state machines may be advanced by more -than one less-deeply nested pattern. Thus, patterns are really DAGs -and not trees. Since patterns are hard enough to understand, the -precise order of evaluation and therefore the behavior of shared -patterns in DAGs may not be well-defined. In this implementation -though, we only call on state machines to advance as needed (we try -not to read out whole periods). - -The next() function gets an item or period by calling :next. - -The :next method is shared by all pattern sub-classes and behaves -differently with :for vs. no :for parameter. With the :for parameter, -we just get items until the count is reached, but getting items is -a bit tricky, because the normal behavior (without :for) might reach -the end of the "natural" period (+eonp+) before count is -reached. So somehow, we need to override count. We could just set -count the count, but count is going to count items and due to -empty periods, count could go to zero before count does. We could -set count = 1000 * count with the idea that we're probably in an -infinite loop generating empty periods forever if count ever reaches -zero. - -But then what about the Heap pattern? If count is greater than the -heap size, what happens when the heap is empty? Or should Heap not -allow :for? There are other "problem" patterns, and not all Vers. 1 -patterns allowed :for, so let's make list of patterns that could use -:for: - -:for is OK :for is not OK ----------- -------------- -cycle heap -line accumulation -random copier -palindrome length -accumulate window -sum -product -eval -markov - -It seems that we could use :for for all patterns and just extend the -behavior a bit, e.g. when the heap runs out, replenish it (without -getting another period from a sub-pattern, if any; accumulation could -just start over; copier could cycle as described earlier; length -really should not allow :for, and window could just generate :for -items before reevaluating :skip and :pattern-length parameters. - -To implement this, the subclass patterns need :advance to do the right -next thing even if we are beyond the "natural" period. :advance should -go to the next sub-pattern or item without returning +eop+ or getting -the next item from any sub-pattern. - -state transitions are based on count and something like this: -count -nil -> actions: :start-period, don't return, set count -N -> N-1, actions: :advance if not have-current, return next item -0 -> -1, actions: return +eop+ --1 -> nil, actions: return +eonp+ - - -def :next() - if length-pattern: // the :for parameter value - if null(count): // initial state before every period - var forcount = next(length-pattern) // must be a number - // compute forcount first and pass to start-period in case there - // is no "natural" period length. If there is a "natural" period, - // the forcount parameter is ignored (the usual case) - self.:start-period(forcount) - have-current = false - // :start-period() sets count, but we replace it with :for parameter - count = forcount - if count == 0: - count = -1 - return +eop+ - if count == -1: - count = nil - return +eonp+ - while true - // otherwise, here is where we return N items - if not have-current - self.:advance() - if not is-nested - // now current is updated - have-current = false - count = count - 1 - return current - // nested, so get item from sub-pattern - rslt = current.:next - if rslt == +eonp+ - // time to advance because sub-pattern has played out - have-current = false - elif rslt == +eop+ - nil // skip ends of periods, we're merging them - // we got a real item to return - else - count = count - 1 - return rslt - // here, we have no length-pattern, so use "natural" periods - // count is null, and we use count - while true - if null(count): - have-current = false - self.:start-period() - if is-nested: - if count == 0: - if merge-flag: // we filtered out +eop+ so return one here - count == -1 - return +eop+ - else - count = nil - return +eonp+ - if count == -1 - count = nil - return +eonp+ - else - if count = 0: - count = -1 - return +eop+ - if count == -1: - count = nil - return +eonp+ - // count is a number > 0 - if not have-current: - self.:advance - have-current = true - if not is-nested - have-current = false - count = count - 1 - return current - // nested, so get sub-pattern's next item or +eonp+ or +eop+ - rslt = current.:next - if rslt == +eonp+ - have-current = false // force advance next time, don't - // return +eonp+ until count == 0 - else if rslt == +eop+ and merge-flag: - nil // iterate, skipping this +eop+ to merge periods - else - return rslt // either +eop+ or a real item - - -If the input is a list of patterns, then the pattern selects patterns -from the list, and the internal state advances as each selected -pattern completes a period. In this case, there is no way to control -the number of elements drawn from each selected pattern -- the number -is always the length of the period returned by the selected -pattern. If :for is specified, this controls the length of the period -delivered to the next less deeply nested pattern, but the delivered -period may be a mix of elements from the more deeply nested patterns. -|# - -(setf SCORE-EPSILON 0.000001) - -(setf pattern-class - (send class :new '(current have-current is-nested name count merge-flag - merge-pattern length-pattern trace))) - -;; sub-classes should all call (send-super :isnew length-pattern name trace) -;; -(send pattern-class :answer :isnew '(mp lp nm tr) - '((setf merge-pattern mp length-pattern lp name nm trace tr) - (xm-traceif "pattern-class :isnew nm" nm "name" name))) - -(defun patternp (x) - (and (objectp x) (send x :isa pattern-class))) - -(setf +eop+ '+eop+) -(setf +eonp+ '+eonp+) ;; end of nested period, this indicates you - ;; should advance yourself and call back to get the next element - -(defun check-for-list (lis name) - (if (not (listp lis)) - (error (format nil "~A, requires a list of elements" name)))) - -(defun check-for-list-or-pattern (lis name) - (if (not (or (listp lis) (patternp lis))) - (error (format nil "~A, requires a list of elements or a pattern" name)))) - -(defun list-has-pattern (lis) - (dolist (e lis) - (if (patternp e) (return t)))) - -(defun is-homogeneous (lis) - (let (type) - (dolist (elem lis t) - (cond ((null type) - (setf type (if (patternp elem) 'pattern 'atom))) - ((and (eq type 'pattern) - (not (patternp elem))) - (return nil)) - ((and (eq type 'atom) - (patternp elem)) - (return nil)))))) - -(defun make-homogeneous (lis traceflag) - (cond ((is-homogeneous lis) lis) - (t - (mapcar #'(lambda (item) - (if (patternp item) item - (make-cycle (list item) - ;; help debugging by naming the new pattern - ;; probably, the name could be item, but - ;; here we coerce item to a string to avoid - ;; surprises in code that assumes string names. - :name (format nil "~A" item) :trace traceflag))) - lis)))) - - -;; used for both "advanced to" and ":next returns" messages -;; -(send pattern-class :answer :write-trace '(verb value) - '((format t "pattern ~A ~A ~A~%" - (if name name "<no-name>") - verb - (if (patternp value) - (if (send value :name) - (send value :name) - "<a-pattern>") - value)))) - - -;; :next returns the next value, including +eop+ and +eonp+ markers -;; -(send pattern-class :answer :next '() - '((xm-traceif ":next of" name "is-nested" is-nested "length-pattern" length-pattern) - (incf xm-next-nesting) - (let ((rslt - (cond (length-pattern (send self :next-for)) - (t (send self :next-natural))))) - (if trace (send self :write-trace ":next returns" rslt)) - (xm-traceif-return ":next" self rslt)))) - - -;; :next-for returns the next value, including +eop+ and +eonp+ markers -;; this code handles the cases where :for is specified, so the length -;; of each period is explicitly given, non intrinsic to the pattern -;; -(send pattern-class :answer :next-for '() - '((block pattern:next-for-block ;; so we can return from inside while loop - (cond ((null count) - (let ((forcount (next length-pattern))) - ;; in the case of window-class, there is no "natural" length - ;; so for that case, we pass in forcount - (send self :start-period forcount) ;; :start-period sets count, - (setf count forcount) ;; but it is replaced here by a number - (setf have-current nil)))) - ;; note that merge-flag is ignored if length-pattern - (cond ((zerop count) - (setf count -1) - (return-from pattern:next-for-block +eop+)) - ((eql count -1) - (setf count nil) - (return-from pattern:next-for-block +eonp+))) - (while t ;; after rejecting special cases, here is where we return N items - (cond ((not have-current) - (send self :advance) - (setf have-current t) - (if trace (send self :write-trace "advanced to" current)))) - (cond ((not is-nested) ;; now current is updated - (setf have-current nil) - (decf count) - (return-from pattern:next-for-block current))) - ;; is-nested, so get item from sub-pattern - (let ((rslt (send current :next))) - (cond ((eq rslt +eonp+) - ;; time to advance because sub-pattern has played out - (setf have-current nil)) - ((eq rslt +eop+)) ;; skip ends of periods; we merge them - (t - (decf count) - (return-from pattern:next-for-block rslt)))))))) - -;; :next-natural returns the next value, including +eop+ and +eonp+ markers -;; this code handles the cases where :for is not specified, so the length -;; of each period is implicitly determined from the pattern -;; -(send pattern-class :answer :next-natural '() - '((block pattern:next-natural-block ;; so we can return from inside while loop - (xm-traceif ":next-natural current" current) - (while t - (cond ((null count) - (setf have-current nil) - ;; :merge parameter is not used by every pattern, but it does not - ;; hurt to evaluate it here - (setf merge-flag (if merge-pattern (next merge-pattern))) - (send self :start-period nil))) ;; sets count - (xm-traceif "count" count "is-nested" is-nested) - (cond (is-nested - (cond ((zerop count) - (cond (merge-flag ;; we filtered out +eop+; return one here - (setf count -1) - (return-from pattern:next-natural-block +eop+)) - (t - (setf count nil) - (return-from pattern:next-natural-block +eonp+)))) - ((eql count -1) - (setf count nil) - (return-from pattern:next-natural-block +eonp+)))) - (t - (cond ((zerop count) - (setf count -1) - (return-from pattern:next-natural-block +eop+)) - ((eql count -1) - (setf count nil) - (return-from pattern:next-natural-block +eonp+))))) - (cond ((not have-current) - (send self :advance) - (setf have-current t) - (if trace (send self :write-trace "advanced to" current)) - (xm-traceif ":advance current" current))) - (cond ((not is-nested) - (setf have-current nil) - (decf count) - (return-from pattern:next-natural-block current))) - ;; nested, so get sub-pattern's next item or +eonp+ or +eop+ - (let ((rslt (send current :next))) - (xm-traceif "in :next-natural got from sub-pattern " rslt) - (cond ((eq rslt +eonp+) - (setf have-current nil) ;; force advance next time, don't - ;; return +eonp+ until count == 0 - (decf count)) - ((and (eq rslt +eop+) merge-flag)) ;; iterate, skip +eop+ - (t - (return-from pattern:next-natural-block rslt)))))))) - - - -(send pattern-class :answer :is-nested '() '(is-nested)) - - -(send pattern-class :answer :name '() '(name)) - - -(send pattern-class :answer :set-current '(c) - '((setf current c) - (let ((value - (if (patternp current) - (send current :name) - current))) - (xm-traceif ":set-current" name "value" value) - ))) - - -;; get-pattern-name - used for debugging, handles non-patterns safely -;; -(defun get-pattern-name (pattern) - (cond ((patternp pattern) (send pattern :name)) - (t pattern))) - - -;; more debugging support -(setf xm-next-nesting -1) -(setf *xm-trace* nil) - -;; use xm-traceif for verbose printing. It only prints if *xm-trace* -;; -(defun xm-traceif (&rest items) - (if *xm-trace* (apply #'xm-trace items))) - -;; use xm-traceif-return for verbose printing of return values. -;; It only prints if *xm-trace*. Includes decrement of xm-next-nesting. -;; -(defun xm-traceif-return (method pattern val) - (xm-traceif method (get-pattern-name pattern) "returning" val) - (decf xm-next-nesting) - val) - -;; use xm-trace for normal tracing enabled by the trace flag in patterns -;; -(defun xm-trace (&rest items) - (princ "|") - (dotimes (i xm-next-nesting) (princ " |")) - (dolist (item items) (princ item) (princ " ")) - (terpri)) - - -;; next -- get the next element in a pattern -;; -;; any non-pattern value is simply returned -;; -(defun next (pattern &optional period-flag) - (incf xm-next-nesting) - (xm-traceif "next" (get-pattern-name pattern) period-flag) - (cond ((and period-flag (patternp pattern)) - (let (rslt elem) - (incf xm-next-nesting) - (xm-traceif "next sending :next to" (get-pattern-name pattern)) - (while (not (eq (setf elem (send pattern :next)) +eop+)) - (xm-traceif "next got" elem "from" (get-pattern-name pattern)) - (if (not (eq elem +eonp+)) - (push elem rslt)) - (if (null elem) (error "got null elem"))) ;;;;;;;; DEBUG ;;;;;;;;;;; - (decf xm-next-nesting) - (xm-traceif-return "next" pattern (reverse rslt)))) - (period-flag - (xm-traceif "next with period-flag" (get-pattern-name pattern)) - (error (format nil "~A, next expected a pattern" - (get-pattern-name pattern)))) - ((patternp pattern) - (xm-traceif "next with pattern" (get-pattern-name pattern) pattern) - (let (rslt) - (dotimes (i 10000 (error - (format nil - "~A, just retrieved 10000 empty periods -- is there a bug?" - (get-pattern-name pattern)))) - (if (not (member (setf rslt (send pattern :next)) - '(+eop+ +eonp+))) - (return (xm-traceif-return "next" pattern rslt)))))) - (t ;; pattern not a pattern, so just return it: - (xm-traceif "next not pattern" pattern) - (xm-traceif-return "next" pattern pattern)))) - -;; ---- LENGTH Class ---- - -(setf length-class - (send class :new '(pattern length-pattern) '() pattern-class)) - -(send length-class :answer :isnew '(p l nm tr) - '((send-super :isnew nil l nm tr) ;; note: no merge pattern is applicable - (setf pattern p))) - -;; note that count is used as a flag as well as a counter. -;; If count is nil, then the pattern-length has not been -;; determined. Count is nil intitially and again at the -;; end of each period. Otherwise, count is an integer -;; used to count down the number of items remaining in -;; the period. - -(send length-class :answer :start-period '(forcount) - '((setf count (next length-pattern)))) - -(send length-class :answer :advance '() - '((send self :set-current (next pattern)))) - -(defun make-length (pattern length-pattern &key (name "length") trace) - (send length-class :new pattern length-pattern name trace)) - -;; ---- CYCLE Class --------- - -(setf cycle-class (send class :new - '(lis cursor lis-pattern) - '() pattern-class)) - -(send cycle-class :answer :isnew '(l mp for nm tr) - '((send-super :isnew mp for nm tr) - (cond ((patternp l) - (setf lis-pattern l)) - ((listp l) - (send self :set-list l tr)) - (t - (error (format nil "~A, expected list" nm) l))))) - - -(send cycle-class :answer :set-list '(l tr) - '((setf lis l) - (check-for-list lis "cycle-class :set-list") - (setf is-nested (list-has-pattern lis)) - (setf lis (make-homogeneous lis tr)))) - - -(send cycle-class :answer :start-period '(forcount) - '((xm-traceif "cycle-class :start-period" "lis-pattern" - (get-pattern-name lis-pattern) "lis" lis "count" count - "length-pattern" (get-pattern-name length-pattern)) - (cond (lis-pattern - (send self :set-list (next lis-pattern t) trace))) - ;; notice that list gets reset at the start of the period - (setf cursor lis) - (if (null count) - (setf count (length lis))))) - - -(send cycle-class :answer :advance '() - '((cond ((and (null cursor) lis) - (setf cursor lis)) - ((null cursor) - (error (format nil "~A, :advance - no items" name)))) - (send self :set-current (car cursor)) - (pop cursor))) - - -(defun make-cycle (lis &key merge for (name "cycle") trace) - (check-for-list-or-pattern lis "make-cycle") - (send cycle-class :new lis merge for name trace)) - -;; ---- LINE class ---- - -(setf line-class (send class :new '(lis cursor lis-pattern) - '() pattern-class)) - -(send line-class :answer :isnew '(l mp for nm tr) - '((send-super :isnew mp for nm tr) - (cond ((patternp l) - (setf lis-pattern l)) - ((listp l) - (send self :set-list l tr)) - (t - (error (format nil "~A, expected list" nm) l))))) - - -(send line-class :answer :set-list '(l tr) - '((setf lis l) - (check-for-list lis "line-class :set-list") - (setf is-nested (list-has-pattern lis)) - (setf lis (make-homogeneous l tr)) - (setf cursor lis))) - - -(send line-class :answer :start-period '(forcount) - '((cond (lis-pattern - (send self :set-list (next lis-pattern t) trace) - (setf cursor lis))) - (if (null count) - (setf count (length lis))))) - - -(send line-class :answer :advance '() - '((cond ((null cursor) - (error (format nil "~A, :advance - no items" name)))) - (send self :set-current (car cursor)) - (if (cdr cursor) (pop cursor)))) - - -(defun make-line (lis &key merge for (name "line") trace) - (check-for-list-or-pattern lis "make-line") - (send line-class :new lis merge for name trace)) - - -;; ---- RANDOM class ----- - -(setf random-class (send class :new - '(lis lis-pattern len previous repeats mincnt maxcnt) - '() pattern-class)) - -;; the structure is (value weight weight-pattern max max-pattern min min-pattern) -(setfn rand-item-value car) -(defun set-rand-item-value (item value) (setf (car item) value)) - -(setfn rand-item-weight cadr) -(defun set-rand-item-weight (item weight) (setf (car (cdr item)) weight)) -(setfn rand-item-weight-pattern caddr) - -(setfn rand-item-max cadddr) -(defun set-rand-item-max (item max) (setf (car (cdddr item)) max)) -(defun rand-item-max-pattern(item) (car (cddddr item))) - -(defun rand-item-min (lis) (cadr (cddddr lis))) -(defun set-rand-item-min (item min) (setf (car (cdr (cddddr item))) min)) -(defun rand-item-min-pattern(item) (car (cddr (cddddr item)))) - - -(defun select-random (len lis previous repeats mincnt maxcnt) - (let (sum items r) - (cond ((zerop len) - (break "random-class has no list to choose from") - nil) - (t - (setf sum 0) - (dolist (item lis) - (setf sum (+ sum (rand-item-weight item)))) - (setf items lis) - (setf r (rrandom)) - (setf sum (* sum r)) - (loop - (setf sum (- sum (rand-item-weight (car items)))) - (if (<= sum 0) (return (car items))) - (setf items (cdr items))))))) - - -(defun random-convert-spec (item) - ;; convert (value :weight wp :min min :max max) to (value nil wp max min) - (let (value (wp 1) minpat maxpat lis) - (setf value (car item)) - (setf lis (cdr item)) - (while lis - (cond ((eq (car lis) :weight) - (setf wp (cadr lis))) - ((eq (car lis) :min) - (setf minpat (cadr lis))) - ((eq (car lis) :max) - (setf maxpat (cadr lis))) - (t - (error "(make-random) item syntax error" item))) - (setf lis (cddr lis))) - (list value nil wp nil maxpat nil minpat))) - - -(defun random-atom-to-list (a) - (if (atom a) - (list a nil 1 nil nil nil nil) - (random-convert-spec a))) - - -(send random-class :answer :isnew '(l mp for nm tr) - ;; there are two things we have to normalize: - ;; (1) make all items lists - ;; (2) if any item is a pattern, make all items patterns - '((xm-traceif "random :isnew list" l "merge" mp "for" for "name" nm "trace" tr) - (send-super :isnew mp for nm tr) - (cond ((patternp l) - (setf lis-pattern l)) - ((listp l) - (send self :set-list l)) - (t - (error (format nil "~A, expected list") l))))) - - -(send random-class :answer :set-list '(l) - '((check-for-list l "random-class :set-list") - (setf lis (mapcar #'random-atom-to-list l)) - ; (display "random set-list" lis) - (dolist (item lis) - (if (patternp (rand-item-value item)) - (setf is-nested t))) - (if is-nested - (mapcar #'(lambda (item) - (if (not (patternp (rand-item-value item))) - (set-rand-item-value item - (make-cycle (list (rand-item-value item)))))) - lis)) - (xm-traceif "random is-new" name lis) - (setf repeats 0) - (setf len (length lis)))) - - -(send random-class :answer :start-period '(forcount) - '((xm-traceif "random-class :start-period" name "count" count "len" len - "lis" lis "lis-pattern" (get-pattern-name lis-pattern)) - (cond (lis-pattern - (send self :set-list (next lis-pattern t)))) - (if (null count) - (setf count len)) - (dolist (item lis) - (set-rand-item-weight item (next (rand-item-weight-pattern item))) - (set-rand-item-max item (next (rand-item-max-pattern item))) - (set-rand-item-min item (next (rand-item-min-pattern item)))) - ; (display "random start-period" lis-pattern lis) - )) - - -(send random-class :answer :advance '() - '((let (selection (iterations 0)) - (xm-traceif "random-class :advance" name "mincnt" mincnt - "repeats" repeats) - (cond ((and mincnt (< repeats mincnt)) - (setf selection previous)) - (t - (setf selection - (select-random len lis previous repeats mincnt maxcnt)))) - (loop ; make sure selection is ok, otherwise try again - (cond ((and (eq selection previous) - maxcnt - (>= repeats maxcnt)) ; hit maximum limit, try again - (setf selection - (select-random len lis previous repeats mincnt maxcnt)) - (incf iterations) - (cond ((> iterations 10000) - (error - (format nil - "~A, unable to pick next item after 10000 tries" - name) - lis)))) - (t (return)))) ; break from loop, we found a selection - - ; otherwise, we are ok - ; notice that we could have selected based on an older maxcnt and - ; maxcnt may now be smaller. This is allowed. Perhaps another - ; rule would be better, e.g. update maxcnt and check against it - ; with each selection. - (if (not (eq selection previous)) - (setf repeats 1) - (incf repeats)) - (setf mincnt (rand-item-min selection)) - (setf maxcnt (rand-item-max selection)) - (setf previous selection) - (xm-traceif "new selection" name "repeats" repeats "mincnt" mincnt - "maxcnt" maxcnt "selection" selection) - (send self :set-current (rand-item-value selection))))) - - -(defun make-random (lis &key merge for (name "random") trace) - (check-for-list-or-pattern lis "make-random") - (send random-class :new lis merge for name trace)) - - -;; ---- PALINDROME class ----- - -#| Palindrome includes elide, which is either t, nil, :first, or :last. -The pattern length is the "natural" length of the pattern, which goes -forward and backward through the list. Thus, if the list is of length N, -the palindrome length depends on elide as follows: - elide length - nil 2N - t 2N - 2 - :first 2N - 1 - :last 2N - 1 -If elide is a pattern, and if length is not specified, then length should -be computed based on elide. -|# - - -(setf palindrome-class (send class :new - '(lis revlis lis-pattern - direction elide-pattern - elide cursor) - '() pattern-class)) - -(send palindrome-class :answer :set-list '(l tr) - '((setf lis l) - (check-for-list lis "palindrome-class :start-period") - (setf is-nested (list-has-pattern lis)) - (setf lis (make-homogeneous l tr)) - (send self :set-cursor))) - -(send palindrome-class :answer :set-cursor '() - '((setf revlis (reverse lis) - direction t - cursor lis))) - - -(send palindrome-class :answer :isnew '(l e mp for nm tr) - '((send-super :isnew mp for nm tr) - (cond ((patternp l) - (setf lis-pattern l)) - ((listp l) - (send self :set-list l tr)) - (t - (error (format nil "~A, expected list" nm) l))) - (setf elide-pattern e))) - - -(send palindrome-class :answer :start-period '(forcount) - '((cond (lis-pattern - (send self :set-list (next lis-pattern t) trace))) - ;; like cycle, list is reset at the start of the period - (send self :set-cursor) - (setf elide (next elide-pattern)) - (if (and elide (null lis)) - (error (format nil "~A, cannot elide if list is empty" name))) - (if (null count) - (setf count (- (* 2 (length lis)) - (if (member elide '(:first :last)) - 1 - (if elide 2 0))))) - (if (<= count 0) - (error (format nil "palindrome ~A period is <= 0" - (get-pattern-name self)))))) - - -(send palindrome-class :answer :next-item '() - '((send self :set-current (car cursor)) - (pop cursor) - (cond ((and cursor (not (cdr cursor)) - (or (and direction (member elide '(:last t))) - (and (not direction) (member elide '(:first t))))) - (pop cursor))))) - - -(send palindrome-class :answer :advance '() - '( - (cond (cursor - (send self :next-item)) - (direction ;; we're going forward - (setf direction nil) ;; now going backward - (setf cursor revlis) - (xm-traceif "palindrome at end" (get-pattern-name self) - "current" (get-pattern-name (car cursor))) - (send self :next-item)) - (t ;; direction is reverse - (setf direction t) - (setf cursor lis) - (send self :next-item))))) - - -(defun make-palindrome (lis &key elide merge for (name "palindrome") trace) - (check-for-list-or-pattern lis "make-palindrome") - (send palindrome-class :new lis elide merge for name trace)) - - -;; ================= HEAP CLASS ====================== - -;; to handle the :max keyword, which tells the object to avoid -;; repeating the last element of the previous period: -;; -;; maxcnt = 1 means "avoid the repetition" -;; check-repeat signals we are at the beginning of the period and must check -;; prev holds the previous value (initially nil) -;; after each item is generated, check-repeat is cleared. It is -;; recalculated when a new period is started. - -(setf heap-class (send class :new '(lis used maxcnt maxcnt-pattern prev - check-repeat lis-pattern len) - '() pattern-class)) - -(send heap-class :answer :isnew '(l mp for mx nm tr) - '((send-super :isnew mp for nm tr) - (cond ((patternp l) - (setf lis-pattern l)) - ((listp l) - ; make a copy of l to avoid side effects - (send self :set-list (append l nil) tr)) - (t - (error (format nil "~A, expected list" nm) l))) - (cond ((patternp mx) - (setf maxcnt-pattern mx)) - ((not (numberp mx)) - (error (format nil "~A, expected number" nm) mx)) - (t - (setf maxcnt mx))))) - - -(send heap-class :answer :set-list '(l tr) - '((setf lis l) - (check-for-list lis "heap-class :set-list") - (setf is-nested (list-has-pattern lis)) - (setf lis (make-homogeneous lis tr)) - (setf len (length lis)))) - - -(send heap-class :answer :start-period '(forcount) - '((xm-traceif "heap-class :start-period" name "lis-pattern" - (get-pattern-name lis-pattern) "count" count "lis" lis) - (cond (lis-pattern - (send self :set-list (next lis-pattern t) trace))) - (cond (maxcnt-pattern - (setf maxcnt (next maxcnt-pattern)))) - ; start of period -- may need to avoid repeating previous item - (if (= maxcnt 1) (setf check-repeat t)) - (if (null count) - (setf count len)))) - - -(defun delete-first (elem lis) - (cond ((null lis) nil) - ((eq elem (car lis)) - (cdr lis)) - (t - (cons (car lis) (delete-first elem (cdr lis)))))) - - -;; NO-DISTINCT-ELEM -- check if any element of list is not val -;; -(defun no-distinct-elem (lis val) - (not - (dolist (elem lis) - (if (not (equal elem val)) - ;; there is a distinct element, return t from dolist - (return t))))) - ;; if no distinct element, dolist returns nil, but this is negated - ;; by the NOT so the function will return t - - -(send heap-class :answer :advance '() - '((cond ((null lis) - (setf lis used) - (setf used nil))) - (let (n elem) - (cond ((and check-repeat (no-distinct-elem lis prev)) - (error (format nil "~A, cannot avoid repetition, but :max is 1" - name)))) - (loop - (setf n (random (length lis))) - (setf elem (nth n lis)) - (if (or (not check-repeat) (not (equal prev elem))) - (return))) ;; loop until suitable element is chosen - (setf lis (delete-first elem lis)) - (push elem used) - (setf check-repeat nil) - (setf prev elem) - (send self :set-current elem)))) - -(defun make-heap (lis &key merge for (max 2) (name "heap") trace) - (send heap-class :new lis merge for max name trace)) - -;;================== COPIER CLASS ==================== - -(setf copier-class (send class :new '(sub-pattern repeat repeat-pattern - period cursor) - '() pattern-class)) - -(send copier-class :answer :isnew '(p r m for nm tr) - '((send-super :isnew m for nm tr) - (setf sub-pattern p repeat-pattern r))) - - -#| copier-class makes copies of periods from sub-pattern - -If merge is true, the copies are merged into one big period. -If merge is false, then repeat separate periods are returned. -If repeat is negative, then -repeat periods of sub-pattern -are skipped. - -merge-flag and repeat are computed from merge-pattern and -repeat-pattern initially and after making repeat copies - -To repeat individual items, set the :for keyword parameter of -the sub-pattern to 1. -|# - -(send copier-class :answer :start-period '(forcount) - '((cond ((null count) - (cond ((or (null repeat) (zerop repeat)) - (send self :really-start-period)) - (t - (setf count (length period)))))))) - - -(send copier-class :answer :really-start-period '() - '((xm-traceif "copier-class :really-start-period" name "count" count) - (setf repeat (next repeat-pattern)) - (while (minusp repeat) - (dotimes (i (- repeat)) - (setf period (next sub-pattern t))) - (setf repeat (next repeat-pattern)) - (setf merge-flag (next merge-pattern))) - -; (print "** STARTING NEXT PATTERN IN COPIER-CLASS") - - (setf period (next sub-pattern t)) - -; (display "copier-class really-start-period got" period) -; (print "** ENDING NEXT PATTERN IN COPIER-CLASS") - - (setf cursor nil) - (if (null count) - (setf count (* (if merge-flag repeat 1) - (length period)))))) - - -(send copier-class :answer :advance '() - '((let ((loop-count 0)) - (loop - (xm-traceif "copier loop" name "repeat" repeat "cursor" cursor - "period" period) - (cond (cursor - (send self :set-current (car cursor)) - (pop cursor) - (return)) - ((plusp repeat) - (decf repeat) - (setf cursor period)) - ((> loop-count 10000) - (error (format nil - "~A, copier-class :advance encountered 10000 empty periods" - name))) - (t - (send self :really-start-period))) - (incf loop-count))))) - - -(defun make-copier (sub-pattern &key for (repeat 1) merge (name "copier") trace) - (send copier-class :new sub-pattern repeat merge for name trace)) - -;; ================= ACCUMULATE-CLASS =================== - -(setf accumulate-class (send class :new '(sub-pattern period cursor sum - mini maxi minimum maximum) - '() pattern-class)) - - -(send accumulate-class :answer :isnew '(p mp for nm tr mn mx) - '((send-super :isnew mp for nm tr) - (setf sub-pattern p sum 0 mini mn maxi mx) - ;(xm-trace "accumulate isnew" self nm) - )) - - -#| -accumulate-class creates sums of numbers from another pattern -The output periods are the same as the input periods (by default). - -(send accumulate-class :answer :start-period '(forcount) - '((cond ((null count) - (send self :really-start-period))))) - -(send accumulate-class :answer :really-start-period '() -|# - - -(send accumulate-class :answer :start-period '(forcount) - '((setf period (next sub-pattern t)) - (setf cursor period) - (xm-traceif "accumulate-class :start-period" name "period" period - "cursor" cursor "count" count) - (if maxi (setf maximum (next maxi))) - (if mini (setf minimum (next mini))) - (if (null count) - (setf count (length period))))) - - -(send accumulate-class :answer :advance '() - '((let ((loop-count 0)) - (loop - (cond (cursor - (setf sum (+ sum (car cursor))) - (cond ((and (numberp minimum) (< sum minimum)) - (setf sum minimum))) - (cond ((and (numberp maximum) (> sum maximum)) - (setf sum maximum))) - (send self :set-current sum) - (pop cursor) - (return)) - ((> loop-count 10000) - (error (format nil - "~A, :advance encountered 10000 empty periods" name))) - (t - (send self :start-period nil))) - (incf loop-count))))) - - -(defun make-accumulate (sub-pattern &key merge for min max (name "accumulate") trace) - (send accumulate-class :new sub-pattern merge for name trace min max)) - -;;================== ACCUMULATION CLASS =================== - -;; for each item, generate all items up to and including the item, e.g. -;; (a b c) -> (a a b a b c) - -(setf accumulation-class (send class :new '(lis lis-pattern outer inner len) - '() pattern-class)) - -(send accumulation-class :answer :isnew '(l mp for nm tr) - '((send-super :isnew mp for nm tr) - (cond ((patternp l) - (setf lis-pattern l)) - ((listp l) - (send self :set-list l)) - (t - (error (format nil "~A, expected list" nm) l))))) - - -(send accumulation-class :answer :set-list '(l) - '((setf lis l) - (check-for-list lis "heap-class :set-list") - (setf lis (make-homogeneous lis trace)) - (setf inner lis) - (setf outer lis) - (setf len (length lis)))) - -(send accumulation-class :answer :start-period '(forcount) - '((cond (lis-pattern - (send self :set-list (next lis-pattern t)))) - ; start of period, length = (n^2 + n) / 2 - (if (null count) (setf count (/ (+ (* len len) len) 2))))) - -(send accumulation-class :answer :advance '() - ;; inner traverses lis from first to outer - ;; outer traverses lis - '((let ((elem (car inner))) - (cond ((eq inner outer) - (setf outer (rest outer)) - (setf outer (if outer outer lis)) - (setf inner lis)) - (t - (setf inner (rest inner)))) - (send self :set-current elem)))) - -(defun make-accumulation (lis &key merge for (name "accumulation") trace) - (send accumulation-class :new lis merge for name trace)) - - -;;================== SUM CLASS ================= - -(setf sum-class (send class :new '(x y period cursor fn) '() pattern-class)) - -(send sum-class :answer :isnew '(xx yy mp for nm tr) - '((send-super :isnew mp for nm tr) - (setf x xx y yy fn #'+))) - -#| -sum-class creates pair-wise sums of numbers from 2 streams. -The output periods are the same as the input periods of the first -pattern argument (by default). -|# - -(send sum-class :answer :start-period '(forcount) - '((cond ((null count) - (send self :really-start-period))))) - -(send sum-class :answer :really-start-period '() - '((setf period (next x t)) - (setf cursor period) - (if (null count) - (setf count (length period))))) - -(send sum-class :answer :advance '() - '((let ((loop-count 0) rslt) - (loop - (cond (cursor - (setf rslt (funcall fn (car cursor) (next y))) - (send self :set-current rslt) - (pop cursor) - (return)) - ((> loop-count 10000) - (error (format nil - "~A, :advance encountered 10000 empty periods" name))) - (t - (send self :really-start-period))) - (incf loop-count))))) - - -(defun make-sum (x y &key merge for (name "sum") trace) - (send sum-class :new x y merge for name trace)) - - -;;================== PRODUCT CLASS ================= - -(setf product-class (send class :new '() '() sum-class)) - -(send product-class :answer :isnew '(xx yy mp for nm tr) - '((send-super :isnew xx yy mp for nm tr) - (setf x xx y yy fn #'*))) - -(defun make-product (x y &key merge for (name "product") trace) - (send product-class :new x y merge for name trace)) - - -;;================== EVAL CLASS ================= -;; -;; (1) if :for, then period is determined by :for and we should -;; just fetch the next item from expr-pattern or use expr -;; (this case is length-pattern) -;; (2) if expr-pattern and not :for, then we should fetch a whole -;; period from expr-pattern and use it to determine period len -;; (this case is (and expr-pattern (not length-pattern))) -;; (3) if not expr-pattern and not :for, then the pattern len is 1 -;; (this case is (and (not expr-pattern) (not length-pattern))) - -(setf eval-class (send class :new '(expr expr-pattern) - '() pattern-class)) - -(send eval-class :answer :isnew '(e mp for nm tr) - '((send-super :isnew mp for nm tr) - (cond ((patternp e) - (setf expr-pattern e)) - (t - (setf expr e))))) - - -(send eval-class :answer :start-period '(forcount) - '((xm-traceif "eval-class :start-period" name "lis-pattern" - (get-pattern-name expr-pattern) "expr" expr "count" count - "length-pattern" (get-pattern-name expr-pattern)) - (cond (length-pattern t) ;; case 1 - (expr-pattern ;; case 2 - (setf expr (next expr-pattern t)) - (setf count (length expr))) - (t ;; case 3 - (setf count 1))))) - - -(send eval-class :answer :advance '() - '((send self :set-current - (cond ((and length-pattern expr-pattern) - (eval (next expr-pattern))) - (length-pattern - (eval expr)) - (expr-pattern - (let ((item (car expr))) - (setf expr (cdr expr)) - item)) - (t (eval expr)))))) - - -(defun make-eval (expr &key merge (for 1) (name "eval") trace) - (send eval-class :new expr merge for name trace)) - -;;================== MARKOV CLASS ==================== - -(setf markov-class (send class :new - '(rules order state produces pattern len) - '() pattern-class)) - - -(defun is-produces-homogeneous (produces) - (let (type elem) - (setf *rslt* nil) - (loop - (cond ((or (null produces) (eq produces :eval) (null (cadr produces))) - (return t))) - (setf elem (cadr produces)) - (cond ((null type) - (setf type (if (patternp elem) 'pattern 'atom)) - (xm-traceif "is-produces-homogeneous type" type) - (setf *rslt* (eq type 'pattern)) - (xm-traceif "is-produces-homogeneous *rslt*" *rslt*) - ) - ((and (eq type 'pattern) (not (patternp elem))) - (return nil)) - ((and (eq type 'atom) - (patternp elem)) - (return nil))) - (setf produces (cddr produces))))) - - -(defun make-produces-homogeneous (produces) - (let (result item) - (loop - (if (null produces) (return nil)) - (push (car produces) result) - (setf produces (cdr produces)) - (setf item (car produces)) - (setf produces (cdr produces)) - (if (not (patternp item)) - (setf item (make-cycle (list item)))) - (push item result)) - (reverse result))) - - -(send markov-class :answer :isnew '(r o s p mp for nm tr) - ;; input parameters are rules, order, state, produces, for, name, trace - '((send-super :isnew mp for nm tr) - (setf order o state s produces p) - (setf len (length r)) - ;; input r looks like this: - ;; ((prev1 prev2 -> next1 next2 (next3 weight) ... ) ...) - ;; transition table will look like a list of these: - ;; ((prev1 prev2 ... prevn) (next1 weight weight-pattern) ...) - (dolist (rule r) - (let ((targets (cdr (nthcdr order rule))) - entry pattern) - ;; build entry in reverse order - (dolist (target targets) - (push (if (atom target) - (list target 1 1) - (list (first target) - (next (second target)) - (second target))) - entry)) - (xm-traceif "markov-class isnew" name "entry" entry "rule" rule - "targets" targets "order" order (nthcdr order rule)) - (dotimes (i order) - (push (nth i rule) pattern)) - (push (cons (reverse pattern) entry) rules))) - (setf rules (reverse rules)) ;; keep rules in original order - (setf *rslt* nil) ;; in case produces is nil - (cond ((and produces (not (is-produces-homogeneous produces))) - (setf produces (make-produces-homogeneous produces)))) - (xm-traceif "markov-class :isnew" name "is-nested" *rslt*) - (setf is-nested *rslt*) ;; returned by is-produces-homogeneous - )) - - -(defun markov-match (state pattern) - (dolist (p pattern t) ;; return true if no mismatch - ;; compare element-by-element - (cond ((eq p '*)) ; anything matches '* - ((eql p (car state))) - (t (return nil))) ; a mismatch: return false - (setf state (cdr state)))) - - -(defun markov-pick-target (sum rule) - (let ((total 0.0) - ;; want to choose a value in the interval [0, sum) - ;; but real-random is not open on the right, so fudge - ;; the range by a small amount: - (r (real-random 0.0 (- sum SCORE-EPSILON)))) - (dolist (target (cdr rule)) - (setf total (+ total (second target))) - (cond ((> total r) (return (car target))))))) - - -(defun markov-update-weights (rule) - (dolist (target (cdr rule)) - (setf (car (cdr target)) (next (caddr target))))) - - -(defun markov-map-target (target produces) - (while (and produces (not (eq target (car produces)))) - (setf produces (cddr produces))) - (let ((rslt (cadr produces))) - (if (not rslt) (setf rslt target)) ;; if lookup fails return target - (if (patternp rslt) (setf rslt (next rslt))) - rslt)) - - -(send markov-class :answer :sum-of-weights '(rule) - '((let ((sum 0.0)) - (dolist (target (cdr rule)) - (xm-traceif "markov-sum-of-weights" name "target" target) - (setf sum (+ sum (second target)))) - sum))) - - -(send markov-class :answer :find-rule '() - '((let (rslt) - (xm-traceif "markov-class find-rule" name "rules" rules) - (dolist (rule rules) - (xm-traceif "markov find-rule" name "state" state "rule" rule) - (cond ((markov-match state (car rule)) - (setf rslt rule) - (return rslt)))) - (cond ((null rslt) - (display "Error, no matching rule found" state rules) - (error (format nil "~A, (markov-class)" name)))) - rslt))) - - -(send markov-class :answer :start-period '(forcount) - '((if (null count) - (setf count len)))) - -(defun markov-general-rule-p (rule) - (let ((pre (car rule))) - (cond ((< (length pre) 2) nil) ;; 1st-order mm - (t - ;; return false if any member not * - ;; return t if all members are * - (dolist (s pre t) - (if (eq s '*) t (return nil))))))) - -(defun markov-find-state-leading-to (target rules) - (let (candidates) - (dolist (rule rules) - (let ((targets (cdr rule))) - (dolist (targ targets) - (cond ((eql (car targ) target) - (push (car rule) candidates)))))) - (cond (candidates ;; found at least one - (nth (random (length candidates)) candidates)) - (t - nil)))) - -(send markov-class :answer :advance '() - '((let (rule sum target rslt new-state) - (xm-traceif "markov :advance" name "pattern" pattern "rules" rules) - (setf rule (send self :find-rule)) - (markov-update-weights rule) - (xm-traceif "markov sum-of-weights" name "rule" rule) - (setf sum (send self :sum-of-weights rule)) - ;; the target can be a pattern, so apply NEXT to it - (setf target (next (markov-pick-target sum rule))) - ;; if the matching rule is multiple *'s, then this - ;; is a higher-order Markov model, and we may now - ;; wander around in parts of the state space that - ;; never appeared in the training data. To avoid this - ;; we violate the strict interpretation of the rules - ;; and pick a random state sequence from the rule set - ;; that might have let to the current state. We jam - ;; this state sequence into state so that when we - ;; append target, we'll have a history that might - ;; have a corresponding rule next time. - (cond ((markov-general-rule-p rule) - (setf new-state (markov-find-state-leading-to target rules)) - (cond (new-state - (xm-trace "markov state replacement" name - "new-state" new-state "target" target) - (setf state new-state))))) - (setf state (append (cdr state) (list target))) - (xm-traceif "markov next" name "rule" rule "sum" sum "target" target - "state" state) - ;; target is the symbol for the current state. We can - ;; return target (default), the value of target, or a - ;; mapped value: - (cond ((eq produces :eval) - (setf target (eval target))) - ((and produces (listp produces)) - (xm-traceif "markov-produce" name "target" target - "produces" produces) - (setf target (markov-map-target target produces)))) - (if (not (eq is-nested (patternp target))) - (error (format nil - "~A :is-nested keyword (~A) not consistent with result (~A)" - name is-nested target))) - (send self :set-current target)))) - - -(defun make-markov (rules &key produces past merge for (name "markov") trace) - ;; check to make sure past and rules are consistent - (let ((order (length past))) - (dolist (rule rules) - (dotimes (i order) - (if (eq (car rule) '->) - (error (format nil "~A, a rule does not match the length of :past" - name))) - (pop rule)) - (if (eq (car rule) '->) nil - (error (format nil "~A, a rule does not match the length of :past" - name))))) - (cond ((null for) - (setf for (length rules)))) - (send markov-class :new rules (length past) past produces merge for name trace)) - - -(defun markov-rule-match (rule state) - (cond ((null state) t) - ((eql (car rule) (car state)) - (markov-rule-match (cdr rule) (cdr state))) - (t nil))) - - -(defun markov-find-rule (rules state) - (dolist (rule rules) - (xm-traceif "markov find-rule" name "rule" rule) - (cond ((markov-rule-match rule state) - (return rule))))) - -;; ------- functions below are for MARKOV-CREATE-RULES -------- - -;; MARKOV-FIND-CHOICE -- given a next state, find it in rule -;; -;; use state to get the order of the Markov model, e.g. how -;; many previous states to skip in the rule, (add 1 for '->). -;; then use assoc to do a quick search -;; -;; example: -;; (markov-find-choice '(a b -> (c 1) (d 2)) '(a b) 'd) -;; returns (d 2) from the rule -;; -(defun markov-find-choice (rule state next) - (assoc next (nthcdr (1+ (length state)) rule))) - -(defun markov-update-rule (rule state next) - (let ((choice (markov-find-choice rule state next))) - (cond (choice - (setf (car (cdr choice)) (1+ (cadr choice)))) - (t - (nconc rule (list (list next 1))))) - rule)) - - -(defun markov-update-rules (rules state next) - (let ((rule (markov-find-rule rules state))) - (cond (rule - (markov-update-rule rule state next)) - (t - (setf rules - (nconc rules - (list (append state - (cons '-> (list - (list next 1))))))))) - rules)) - - -;; MARKOV-UPDATE-HISTOGRAM -- keep a list of symbols and counts -;; -;; This histogram will become the right-hand part of a rule, so -;; the format is ((symbol count) (symbol count) ...) -;; -(defun markov-update-histogram (histogram next) - (let ((pair (assoc next histogram))) - (cond (pair - (setf (car (cdr pair)) (1+ (cadr pair)))) - (t - (setf histogram (cons (list next 1) histogram)))) - histogram)) - - -(defun markov-create-rules (sequence order &optional generalize) - (let ((seqlen (length sequence)) state rules next histogram rule) - (cond ((<= seqlen order) - (error "markov-create-rules: sequence must be longer than order")) - ((< order 1) - (error "markov-create-rules: order must be 1 or greater"))) - ; build initial state sequence - (dotimes (i order) - (setf state (nconc state (list (car sequence)))) - (setf sequence (cdr sequence))) - ; for each symbol, either update a rule or add a rule - (while sequence - (setf next (car sequence)) - (setf sequence (cdr sequence)) - (setf rules (markov-update-rules rules state next)) - (setf histogram (markov-update-histogram histogram next)) - ; shift next state onto current state list - (setf state (nconc (cdr state) (list next)))) - ; generalize? - (cond (generalize - (setf rule (cons '-> histogram)) - (dotimes (i order) - (setf rule (cons '* rule))) - (setf rules (nconc rules (list rule))))) - rules)) - - -;; ----- WINDOW Class --------- - -(setf window-class (send class :new - '(pattern skip-pattern lis cursor) - '() pattern-class)) - -(send window-class :answer :isnew '(p for sk nm tr) - '((send-super :isnew nil for nm tr) - (setf pattern p skip-pattern sk))) - - -(send window-class :answer :start-period '(forcount) - '((if (null length-pattern) - (error (format nil "~A, :start-period -- length-pattern is null" - name))) - (setf count forcount) - (cond ((null lis) ;; first time - (dotimes (i count) - (push (next pattern) lis)) - (setf lis (reverse lis)) - (setf cursor lis)) - (t - (let ((skip (next skip-pattern))) - (dotimes (i skip) - (if lis (pop lis) (next pattern)))) - (setf lis (reverse lis)) - ;; now lis is in reverse order; if not long enough, push - (let ((len (length lis)) rslt) - (while (< len count) - (incf len) - (push (next pattern) lis)) - (setf lis (reverse lis)) - ;; lis is in order, copy it to rstl and take what we need - (setf rslt (reverse (append lis nil))) ;; copy lis - (while (> len count) - (decf len) - (pop rslt)) - (setf cursor (reverse rslt))))) - (xm-traceif "window start-period cursor" cursor "lis" lis))) - - -(send window-class :answer :advance '() - '((send self :set-current (car cursor)) - (pop cursor))) - -(defun make-window (pattern length-pattern skip-pattern - &key (name "window") trace) - (send window-class :new pattern length-pattern skip-pattern name trace)) - -;; SCORE-SORTED -- test if score is sorted -;; -(defun score-sorted (score) - (let ((result t)) - (while (cdr score) - (cond ((event-before (cadr score) (car score)) - (setf result nil) - (return nil))) - (setf score (cdr score))) - result)) - - -(defmacro score-gen (&rest args) - (let (key val tim dur (name ''note) ioi trace save - score-len score-dur others pre post - next-expr (score-begin 0) score-end) - (while (and args (cdr args)) - (setf key (car args)) - (setf val (cadr args)) - (setf args (cddr args)) - (case key - (:time (setf tim val)) - (:dur (setf dur val)) - (:name (setf name val)) - (:ioi (setf ioi val)) - (:trace (setf trace val)) - (:save (setf save val)) - (:pre (setf pre val)) - (:post (setf post val)) - (:score-len (setf score-len val)) - (:score-dur (setf score-dur val)) - (:begin (setf score-begin val)) - (:end (setf score-end val)) - (t (setf others (cons key (cons val others)))))) - ;; make sure at least one of score-len, score-dur is present - (cond ((and (null score-len) (null score-dur)) - (error - "score-gen needs either :score-len or :score-dur to limit length"))) - ;; compute expression for dur - (cond ((null dur) - (setf dur 'sg:ioi))) - ;; compute expression for ioi - (cond ((null ioi) - (setf ioi 1))) - ;; compute expression for next start time - (setf next-expr '(+ sg:start sg:ioi)) - ; (display "score-gen" others) - `(let (sg:seq (sg:start ,score-begin) sg:ioi - (sg:score-len ,score-len) (sg:score-dur ,score-dur) - (sg:count 0) (sg:save ,save) - (sg:begin ,score-begin) (sg:end ,score-end) sg:det-end) - ;; sg:det-end is a flag that tells us to determine the end time - (cond ((null sg:end) (setf sg:end 0 sg:det-end t))) - ;; make sure at least one of score-len, score-dur is present - (loop - (cond ((or (and sg:score-len (<= sg:score-len sg:count)) - (and sg:score-dur (<= (+ sg:begin sg:score-dur) sg:start))) - (return))) - ,pre - ,(cond (tim (list 'setf 'sg:start tim))) - (setf sg:ioi ,ioi) - (setf sg:dur ,dur) - (push (list sg:start sg:dur (list ,name ,@others)) - sg:seq) - ,post - (cond (,trace - (format t "get-seq trace at ~A stretch ~A: ~A~%" - sg:start sg:dur (car sg:seq)))) - (incf sg:count) - (setf sg:start ,next-expr) - ;; end time of score will be max over start times of the next note - ;; this bases the score duration on ioi's rather than durs. But - ;; if user specified sg:end, sg:det-end is false and we do not - ;; try to compute sg:end. - (cond ((and sg:det-end (> sg:start sg:end)) - (setf sg:end sg:start)))) - (setf sg:seq (reverse sg:seq)) - ;; avoid sorting a sorted list -- XLisp's quicksort can overflow the - ;; stack if the list is sorted because (apparently) the pivot points - ;; are not random. - (cond ((not (score-sorted sg:seq)) - (setf sg:seq (bigsort sg:seq #'event-before)))) - (push (list 0 0 (list 'SCORE-BEGIN-END ,score-begin sg:end)) sg:seq) - (cond (sg:save (set sg:save sg:seq))) - sg:seq))) - -;; ============== score manipulation =========== - -(defun must-be-valid-score (caller score) - (if (not (score-validp score)) - (error (strcat "In " caller ", not a valid score") score))) - -(defun invalid-score () (return-from validp nil)) -(defun score-validp (score) - (block validp - (if (listp score) nil (invalid-score)) ;; tricky: return nil if NOT condition - (dolist (event score) - (if (listp event) nil (invalid-score)) - (if (and (event-time event) (numberp (event-time event))) nil - (invalid-score)) - (if (and (event-dur event) (numberp (event-dur event))) nil - (invalid-score)) - (if (and (event-expression event) (consp (event-expression event))) nil - (invalid-score))) - t)) - -(defun event-before (a b) - (< (car a) (car b))) - -;; EVENT-END -- get the ending time of a score event -;; -(defun event-end (e) (+ (car e) (cadr e))) - -;; EVENT-TIME -- time of an event -;; -(setfn event-time car) - -;; EVENT-DUR -- duration of an event -;; -(setfn event-dur cadr) - -;; EVENT-SET-TIME -- new event with new time -;; -(defun event-set-time (event time) - (cons time (cdr event))) - - -;; EVENT-SET-DUR -- new event with new dur -;; -(defun event-set-dur (event dur) - (list (event-time event) - dur - (event-expression event))) - - -;; EVENT-SET-EXPRESSION -- new event with new expression -;; -(defun event-set-expression (event expression) - (list (event-time event) - (event-dur event) - expression)) - -;; EXPR-HAS-ATTR -- test if expression has attribute -;; -(defun expr-has-attr (expression attr) - (member attr expression)) - - -;; EXPR-GET-ATTR -- get value of attribute from expression -;; -(defun expr-get-attr (expression attr &optional default) - (let ((m (member attr expression))) - (if m (cadr m) default))) - - -;; EXPR-SET-ATTR -- set value of an attribute in expression -;; (returns new expression) -(defun expr-set-attr (expr attr value) - (cons (car expr) (expr-parameters-set-attr (cdr expr) attr value))) - -(defun expr-parameters-set-attr (lis attr value) - (cond ((null lis) (list attr value)) - ((eq (car lis) attr) (cons attr (cons value (cddr lis)))) - (t (cons (car lis) - (cons (cadr lis) - (expr-parameters-set-attr (cddr lis) attr value)))))) - - -;; EXPR-REMOVE-ATTR -- expression without attribute value pair -(defun expr-remove-attr (event attr) - (cons (car expr) (expr-parameters-remove-attr (cdr expr) attr))) - -(defun expr-parameters-remove-attr (lis attr) - (cond ((null lis) nil) - ((eq (car lis) attr) (cddr lis)) - (t (cons (car lis) - (cons (cadr lis) - (expr-parameters-remove-attr (cddr lis) attr)))))) - - -;; EVENT-GET-ATTR -- get value of attribute from event -;; -(defun event-get-attr (note attr &optional default) - (expr-get-attr (event-expression note) attr default)) - - -;; EVENT-SET-ATTR -- new event with attribute = value -(defun event-set-attr (event attr value) - (event-set-expression - event - (expr-set-attr (event-expression event) attr value))) - - -;; EVENT-REMOVE-ATTR -- new event without attribute value pair -(defun event-remove-attr (event attr) - (event-set-expression - event - (event-remove-attr (event-expression event) attr))) - - -;; SCORE-GET-BEGIN -- get the begin time of a score -;; -(defun score-get-begin (score) - (setf score (score-must-have-begin-end score)) - (cadr (event-expression (car score)))) - - -;; SCORE-SET-BEGIN -- set the begin time of a score -;; -(defun score-set-begin (score time) - (setf score (score-must-have-begin-end score)) - (cons (list 0 0 (list 'score-begin-end time - (caddr (event-expression (car score))))) - (cdr score))) - - -;; SCORE-GET-END -- get the end time of a score -;; -(defun score-get-end (score) - (setf score (score-must-have-begin-end score)) - (caddr (event-expression (car score)))) - - -;; SCORE-SET-END -- set the end time of a score -;; -(defun score-set-end (score time) - (setf score (score-must-have-begin-end score)) - (cons (list 0 0 (list 'score-begin-end - (cadr (event-expression (car score))) time)) - (cdr score))) - - -;; FIND-FIRST-NOTE -- use keywords to find index of first selected note -;; -(defun find-first-note (score from-index from-time) - (let ((s (cdr score))) - ;; offset by one because we removed element 0 - (setf from-index (if from-index (max 0 (- from-index 1)) 0)) - (setf from-time (if from-time - (- from-time SCORE-EPSILON) - (- SCORE-EPSILON))) - (if s (setf s (nthcdr from-index s))) - - (while (and s (>= from-time (event-time (car s)))) - (setf s (cdr s)) - (incf from-index)) - (1+ from-index))) - - -;; EVENT-BEFORE -- useful function for sorting scores -;; -(defun event-before (a b) - (< (car a) (car b))) - -;; bigsort -- a sort routine that avoids recursion in order -;; to sort large lists without overflowing the evaluation stack -;; -;; Does not modify input list. Does not minimize cons-ing. -;; -;; Algorithm: first accumulate sorted sub-sequences into lists -;; Then merge pairs iteratively until only one big list remains -;; -(defun bigsort (lis cmp) ; sort lis using cmp function - ;; if (funcall cmp a b) then a and b are in order - (prog (rslt sub pairs) - ;; first, convert to sorted sublists stored on rslt - ;; accumulate sublists in sub - get-next-sub - (if (null lis) (go done-1)) - (setf sub (list (car lis))) - (setf lis (cdr lis)) - fill-sub - ;; invariant: sub is non-empty, in reverse order - (cond ((and lis (funcall cmp (car sub) (car lis))) - (setf sub (cons (car lis) sub)) - (setf lis (cdr lis)) - (go fill-sub))) - (setf sub (reverse sub)) ;; put sub in correct order - (setf rslt (cons sub rslt)) ;; build rslt in reverse order - (go get-next-sub) - done-1 - ;; invariant: rslt is list of sorted sublists - (if (cdr rslt) nil (go done-2)) - ;; invariant: rslt has at least one list - (setf pairs rslt) - (setf rslt nil) - merge-pairs ;; merge a pair and save on rslt - (if (car pairs) nil (go end-of-pass)) ;; loop until all pairs merged - ;; invariant: pairs has at least one list - (setf list1 (car pairs)) ;; list1 is non-empty - (setf list2 (cadr pairs)) ;; list2 could be empty - (setf pairs (cddr pairs)) - (cond (list2 - (setf rslt (cons (list-merge list1 list2 cmp) rslt))) - (t - (setf rslt (cons list1 rslt)))) - (go merge-pairs) - end-of-pass - (go done-1) - done-2 - ;; invariant: rslt has one sorted list! - (return (car rslt)))) - -(defun list-merge (list1 list2 cmp) - (prog (rslt) - merge-loop - (cond ((and list1 list2) - (cond ((funcall cmp (car list1) (car list2)) - (setf rslt (cons (car list1) rslt)) - (setf list1 (cdr list1))) - (t - (setf rslt (cons (car list2) rslt)) - (setf list2 (cdr list2))))) - (list1 - (return (nconc (reverse rslt) list1))) - (t - (return (nconc (reverse rslt) list2)))) - (go merge-loop))) - - -;; SCORE-SORT -- sort a score into time order -;; -;; If begin-end exists, preserve it. If not, compute -;; it from the sorted score. -;; -(defun score-sort (score &optional (copy-flag t)) - (let* ((score1 (score-must-have-begin-end score)) - (begin-end (car score1)) - ;; if begin-end already existed, then it will - ;; be the first of score. Otherwise, one must - ;; have been generated above by score-must-have-begin-end - ;; in which case we should create it again after sorting. - (needs-begin-end (not (eq begin-end (first score))))) - (setf score1 (cdr score1)) ;; don't include begin-end in sort. - (if copy-flag (setf score1 (append score1 nil))) - (setf score1 (bigsort score1 #'event-before)) - (if needs-begin-end (score-must-have-begin-end score1) - (cons begin-end score1)) - )) - - -;; PUSH-SORT -- insert an event in (reverse) sorted order -;; -;; Note: Score should NOT have a score-begin-end expression -;; -(defun push-sort (event score) - (let (insert-after) - (cond ((null score) (list event)) - ((event-before (car score) event) - (cons event score)) - (t - (setf insert-after score) - (while (and (cdr insert-after) - (event-before event (cadr insert-after))) - (setf insert-after (cdr insert-after))) - (setf (cdr insert-after) (cons event (cdr insert-after))) - score)))) - - -(setf FOREVER 3600000000.0) ; 1 million hours - -;; FIND-LAST-NOTE -- use keywords to find index beyond last selected note -;; -;; note that the :to-index keyword is the index of the last note (numbered -;; from zero), whereas this function returns the index of the last note -;; plus one, i.e. selected notes have an index *less than* this one -;; -(defun find-last-note (score to-index to-time) - ;; skip past score-begin-end event - (let ((s (cdr score)) - (n 1)) - (setf to-index (if to-index (1+ to-index) (length score))) - (setf to-time (if to-time (- to-time SCORE-EPSILON) FOREVER)) - (while (and s (< n to-index) (< (event-time (car s)) to-time)) - (setf s (cdr s)) - (incf n)) - n)) - - -;; SCORE-MUST-HAVE-BEGIN-END -- add score-begin-end event if necessary -;; -(defun score-must-have-begin-end (score) - (cond ((null score) - (list (list 0 0 (list 'SCORE-BEGIN-END 0 0)))) - ((eq (car (event-expression (car score))) 'SCORE-BEGIN-END) - score) - (t (cons (list 0 0 (list 'SCORE-BEGIN-END (event-time (car score)) - (event-end (car (last score))))) - score)))) - - -;; SCORE-SHIFT -- add offset to times of score events -;; -(defun score-shift (score offset &key from-index to-index from-time to-time) - (setf score (score-must-have-begin-end score)) - (let ((i 1) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time)) - (begin (cadr (event-expression (car score)))) - (end (caddr (event-expression (car score)))) - result) - (dolist (event (cdr score)) - (cond ((and (<= start i) (< i stop)) - (setf event (event-set-time - event (+ (event-time event) offset))) - (setf begin (min begin (event-time event))) - (setf end (max end (event-end event))))) - (setf result (push-sort event result)) - (incf i)) - (cons (list 0 0 (list 'SCORE-BEGIN-END begin end)) - (reverse result)))) - - -;; TIME-STRETCH -- map a timestamp according to stretch factor -;; -(defun time-stretch (time stretch start-time stop-time) - (cond ((< time start-time) time) - ((< time stop-time) - (+ start-time (* stretch (- time start-time)))) - (t ; beyond stop-time - (+ (- time stop-time) ; how much beyond stop-time - start-time - (* stretch (- stop-time start-time)))))) - - -;; EVENT-STRETCH -- apply time warp to an event -(defun event-stretch (event stretch dur-flag time-flag start-time stop-time) - (let* ((new-time (event-time event)) - (new-dur (event-dur event)) - (end-time (+ new-time new-dur))) - (cond (time-flag - (setf new-time (time-stretch new-time stretch - start-time stop-time)))) - (cond ((and time-flag dur-flag) - ;; both time and dur are stretched, so map the end time just - ;; like the start time, then subtract to get new duration - (setf end-time (time-stretch end-time stretch - start-time stop-time)) - (setf new-dur (- end-time new-time))) - ((and dur-flag (>= new-time start-time) (< new-time stop-time)) - ;; stretch only duration, not time. If note starts in range - ;; scale to get the new duration. - (setf new-dur (* stretch new-dur)))) - (list new-time new-dur (event-expression event)))) - - -;; SCORE-STRETCH -- stretch a region of the score -;; -(defun score-stretch (score factor &key (dur t) (time t) - from-index to-index (from-time 0) (to-time FOREVER)) - (if (zerop factor) (print "WARNING: score-stretch called with zero stretch factor.")) - (setf score (score-must-have-begin-end score)) - (let ((begin-end (event-expression (car score))) - (i 1)) - (if from-index - (setf from-time (max from-time - (event-time (nth from-index score))))) - (if to-index - (setf to-time (min to-time - (event-end (nth to-index score))))) - ; stretch from start-time to stop-time - (cons (list 0 0 (list 'SCORE-BEGIN-END - (time-stretch (cadr begin-end) factor - from-time to-time) - (time-stretch (caddr begin-end) factor - from-time to-time))) - (mapcar #'(lambda (event) - (event-stretch event factor dur time - from-time to-time)) - (cdr score))))) - - -;; Turn a value field into a numeric value if possible -;; (by looking up a global variable binding). This -;; allows scores to say C4 instead of 60. -;; -(defun get-numeric-value (v) - (cond ((and v (symbolp v) (boundp v) (numberp (symbol-value v))) - (symbol-value v)) - (t v))) - - -(defun params-transpose (params keyword amount) - (cond ((null params) nil) - ((eq keyword (car params)) - (let ((v (get-numeric-value (cadr params)))) - (cond ((numberp v) - (setf v (+ v amount))) - ((and (eq keyword :pitch) (listp v)) - (setf v (mapcar #'(lambda (x) (setf x (get-numeric-value x)) - (+ x amount)) v)))) - (cons (car params) - (cons v (cddr params))))) - (t (cons (car params) - (cons (cadr params) - (params-transpose (cddr params) keyword amount)))))) - - -(defun score-transpose (score keyword amount &key - from-index to-index from-time to-time) - (score-apply score - #'(lambda (time dur expression) - (list time dur - (cons (car expression) - (params-transpose (cdr expression) - keyword amount)))) - :from-index from-index :to-index to-index - :from-time from-time :to-time to-time)) - - -(defun params-scale (params keyword amount) - (cond ((null params) nil) - ((eq keyword (car params)) - (let ((v (get-numeric-value (cadr params)))) - (cond ((numberp v) - (setf v (* v amount)))) - (cons (car params) - (cons v (cddr params))))) - (t (cons (car params) - (cons (cadr params) - (params-scale (cddr params) keyword amount)))))) - - -(defun score-scale (score keyword amount &key - from-index to-index from-time to-time) - (score-apply score - #'(lambda (time dur expression) - (list time dur - (cons (car expression) - (params-scale (cdr expression) - keyword amount)))) - :from-index from-index :to-index to-index - :from-time from-time :to-time to-time)) - - -(defun score-sustain (score factor &key - from-index to-index from-time to-time) - (setf score (score-must-have-begin-end score)) - (let ((i 0) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time)) - result) - (dolist (event score) - (cond ((and (<= start i) (< i stop)) - (setf event (event-set-dur - event (* (event-dur event) factor))))) - (push event result) - (incf i)) - (reverse result))) - - -;; MAP-VOICE - helper function for SCORE-VOICE -;; input: a score expression, e.g. '(note :pitch 60 :vel 100) -;; a replacement list, e.g. '((note foo) (* bar)) -;; output: the score expression with substitutions, e.g. -;; '(foo :pitch 60 :vel 100) -;; -(defun map-voice (expression replacement-list) - (cond (replacement-list - (cond ((or (eq (car expression) (caar replacement-list)) - (eq (caar replacement-list) '*)) - (cons (cadar replacement-list) (cdr expression))) - (t (map-voice expression (cdr replacement-list))))) - (t expression))) - - -(defun ny:assert-replacement-list (fun-name index formal actual) - (let ((lis actual) r) - (while lis - (if (not (consp actual)) - (error (format nil "In ~A,~A argument (~A) should be a list, got ~A" - fun-name (index-to-string index) formal actual))) - (setf r (car lis)) - (if (not (and (listp r) (= 2 (length r)) (symbolp (car r)) (symbolp (cadr r)))) - (error (format nil - "In ~A,~A argument (~A) should be a list of lists of two symbols, got ~A" - fun-name (index-to-string index) formal actual))) - (setf lis (cdr lis)) ))) - - -(defun score-voice (score replacement-list &key - from-index to-index from-time to-time) - (ny:assert-replacement-list 'SCORE-VOICE 2 "replacement-list" replacement-list) - (setf score (score-must-have-begin-end score)) - (let ((i 0) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time)) - result) - (dolist (event score) - (cond ((and (<= start i) (< i stop)) - (setf event (event-set-expression - event (map-voice (event-expression event) - replacement-list))))) - (push event result) - (incf i)) - (reverse result))) - - -(defun score-merge (&rest scores) - ;; scores is a list of scores - (cond ((null scores) nil) - (t - (score-merge-1 (car scores) (cdr scores))))) - - -;; SCORE-MERGE-1 -- merge list of scores into score -;; -(defun score-merge-1 (score scores) - ;; scores is a list of scores to merge - (cond ((null scores) score) - (t (score-merge-1 (score-merge-2 score (car scores)) - (cdr scores))))) - -;; SCORE-MERGE-2 -- merge 2 scores -;; -(defun score-merge-2 (score addin) - ;(display "score-merge-2 before" score addin) - (setf score (score-must-have-begin-end score)) - (setf addin (score-must-have-begin-end addin)) - ;(display "score-merge-2" score addin) - (let (start1 start2 end1 end2) - (setf start1 (score-get-begin score)) - (setf start2 (score-get-begin addin)) - (setf end1 (score-get-end score)) - (setf end2 (score-get-end addin)) - - ;; note: score-sort is destructive, but append copies score - ;; and score-shift copies addin - (score-sort - (cons (list 0 0 (list 'SCORE-BEGIN-END (min start1 start2) - (max end1 end2))) - (append (cdr score) (cdr addin) nil))))) - - - -;; SCORE-APPEND -- append scores together in sequence -;; -(defun score-append (&rest scores) - ;; scores is a list of scores - (cond ((null scores) nil) - (t - (score-append-1 (car scores) (cdr scores))))) - - -;; SCORE-APPEND-1 -- append list of scores into score -;; -(defun score-append-1 (score scores) - ;; scores is a list of scores to append - (cond ((null scores) score) - (t (score-append-1 (score-append-2 score (car scores)) - (cdr scores))))) - - -;; SCORE-APPEND-2 -- append 2 scores -;; -(defun score-append-2 (score addin) - ;(display "score-append-2" score addin) - (setf score (score-must-have-begin-end score)) - (setf addin (score-must-have-begin-end addin)) - (let (end1 start2 begin-end1 begin-end2) - (setf start1 (score-get-begin score)) - (setf end1 (score-get-end score)) - (setf start2 (score-get-begin addin)) - (setf end2 (score-get-end addin)) - (setf begin-end1 (event-expression (car score))) - (setf begin-end2 (event-expression (car addin))) - (setf addin (score-shift addin (- end1 start2))) - ;; note: score-sort is destructive, but append copies score - ;; and score-shift copies addin - (score-sort - (cons (list 0 0 (list 'SCORE-BEGIN-END start1 (+ end1 (- end2 start2)))) - (append (cdr score) (cdr addin) nil))))) - - -(defun score-select (score predicate &key - from-index to-index from-time to-time reject) - (setf score (score-must-have-begin-end score)) - (let ((begin-end (car score)) - (i 1) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time)) - result) - ;; selected if start <= i AND i < stop AND predicate(...) - ;; choose if not reject and selected or reject and not selected - ;; so in other words choose if reject != selected. Use NULL to - ;; coerce into boolean values and then use NOT EQ to compare - (dolist (event (cdr score)) - (cond ((not (eq (null reject) - (null (and (<= start i) (< i stop) - (or (eq predicate t) - (funcall predicate - (event-time event) - (event-dur event) - (event-expression event))))))) - (push event result))) - (incf i)) - (cons begin-end (reverse result)))) - - -;; SCORE-FILTER-LENGTH -- remove notes beyond cutoff time -;; -(defun score-filter-length (score cutoff) - (let (result) - (dolist (event score) - (cond ((<= (event-end event) cutoff) - (push event result)))) - (reverse result))) - - -;; SCORE-REPEAT -- make n copies of score in sequence -;; -(defun score-repeat (score n) - (let (result) - (dotimes (i n) - (setf result (score-append result score))) - result)) - - -;; SCORE-STRETCH-TO-LENGTH -- stretch score to have given length -;; -(defun score-stretch-to-length (score length) - (let ((begin-time (score-get-begin score)) - (end-time (score-get-end score)) - duration stretch) - (setf duration (- end-time begin-time)) - (cond ((< 0 duration) - (setf stretch (/ length (- end-time begin-time))) - (score-stretch score stretch)) - (t score)))) - - -(defun score-filter-overlap (score) - (setf score (score-must-have-begin-end score)) - (prog (event end-time filtered-score - (begin-end (car score))) - (setf score (cdr score)) - (cond ((null score) (return (list begin-end)))) - loop - ;; get event from score - (setf event (car score)) - ;; add a note to filtered-score - (push event filtered-score) - ;; save the end-time of this event: start + duration - (setf end-time (+ (car event) (cadr event))) - ;; now skip everything until end-time in score - loop2 - (pop score) ;; move to next event in score - (cond ((null score) - (return (cons begin-end (reverse filtered-score))))) - (setf event (car score)) ;; examine next event - (setf start-time (car event)) - ;(display "overlap" start-time (- end-time SCORE-EPSILON)) - (cond ((< start-time (- end-time SCORE-EPSILON)) - ;(display "toss" event start-time end-time) - (go loop2))) - (go loop))) - - -(defun score-print (score &optional lines) - (let ((len (length score))) ;; len will be how many events left - (format t "(") - (cond (lines - (setf lines (max lines 3))) ;; always allow up to 3 lines - (t ;; no limit on lines, pick a conservatively large number - (setf lines (+ 100 len)))) - (dolist (event score) - (cond ((or (> lines 2) (= 1 len)) - ;; print if we have more than 2 lines left to print or - ;; if we are at the last line (always printed) - (format t "~S~%" event) - (setf lines (1- lines))) - ((and (= lines 2) (> len 2)) ;; need ellipsis - (format t "... skipping ~A events ...~%" (- len lines)) - (setf lines (1- lines))) - (t nil)) ;; print nothing until end if lines is 1 - (setf len (1- len))) - (format t ")~%"))) - -(defun score-play (score) - (play (timed-seq score))) - - -(defun score-adjacent-events (score function &key - from-index to-index from-time to-time) - (setf score (score-must-have-begin-end score)) - (let ((begin-end (car score)) - (a nil) - (b (second score)) - (c-list (cddr score)) - r newscore - (i 1) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time))) - (dolist (event (cdr score)) - (setf r b) - (cond ((and (<= start i) (< i stop)) - (setf r (funcall function a b (car c-list))))) - (cond (r - (push r newscore) - (setf a r))) - (setf b (car c-list)) - (setf c-list (cdr c-list)) - (incf i)) - (score-sort (cons begin-end newscore)))) - - -(defun score-apply (score fn &key - from-index to-index from-time to-time) - - (setf score (score-must-have-begin-end score)) - (let ((begin-end (car score)) - (i 1) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time)) - result) - (dolist (event (cdr score)) - (push - (cond ((and (<= start i) (< i stop)) - (funcall fn (event-time event) - (event-dur event) (event-expression event))) - (t event)) - result) - (incf i)) - (score-sort (cons begin-end result)))) - - -(defun score-indexof (score fn &key - from-index to-index from-time to-time) - (setf score (score-must-have-begin-end score)) - (let ((i 1) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time)) - result) - (dolist (event (cdr score)) - (cond ((and (<= start i) (< i stop) - (funcall fn (event-time event) - (event-dur event) - (event-expression event))) - (setf result i) - (return))) - (incf i)) - result)) - - -(defun score-last-indexof (score fn &key - from-index to-index from-time to-time) - (setf score (score-must-have-begin-end score)) - (let ((i 1) - (start (find-first-note score from-index from-time)) - (stop (find-last-note score to-index to-time)) - result) - (dolist (event (cdr score)) - (cond ((and (<= start i) (< i stop) - (funcall fn (event-time event) - (event-dur event) - (event-expression event))) - (setf result i))) - (incf i)) - result)) - - -;; SCORE-RANDOMIZE-START -- alter start times with offset -;; keywords: jitter, offset, feel factor -;; -(defun score-randomize-start (score amt &key - from-index to-index from-time to-time) - (score-apply score - (lambda (time dur expr) - (setf time (+ (real-random (- amt) amt) time)) - (setf time (max 0.0 time)) - (list time dur expr)))) - - -;; SCORE-READ-SMF -- read a standard MIDI file to a score -;; -(defun score-read-smf (filename) - (let ((seq (seq-create)) - (file (open-binary filename))) - (cond (file - (seq-read-smf seq file) - (close file) - (score-from-seq seq)) - (t nil)))) - - -;; SCORE-READ -- read a standard MIDI file to a score -;; -(defun score-read (filename) - (let ((seq (seq-create)) - (file (open filename))) - (cond (file - (seq-read seq file) - (close file) - (score-from-seq seq)) - (t nil)))) - - -;; SET-PROGRAM-TO -- a helper function to set a list value -(defun set-program-to (lis index value default) - ;; if length or lis <= index, extend the lis with default - (while (<= (length lis) index) - (setf lis (nconc lis (list default)))) - ;; set the nth element - (setf (nth index lis) value) - ;; return the list - lis) - - -(defun score-from-seq (seq) - (prog (event tag score programs) - (seq-reset seq) -loop - (setf event (seq-get seq)) - (setf tag (seq-tag event)) - (cond ((= tag seq-done-tag) - (go exit)) - ((= tag seq-prgm-tag) - (let ((chan (seq-channel event)) - (when (seq-time event)) - (program (seq-program event))) - (setf programs (set-program-to programs chan program 0)) - (push (list (* when 0.001) 1 - (list 'NOTE :pitch nil :program program)) - score))) - ((= tag seq-note-tag) - (let ((chan (seq-channel event)) - (pitch (seq-pitch event)) - (vel (seq-velocity event)) - (when (seq-time event)) - (dur (seq-duration event))) - (push (list (* when 0.001) (* dur 0.001) - (list 'NOTE :chan (1- chan) :pitch pitch :vel vel)) - score)))) - (seq-next seq) - (go loop) -exit - (setf *rslt* programs) ;; extra return value - (return (score-sort score)))) - - -(defun score-write (score filename &optional programs absolute) - (score-write-smf score filename programs t absolute)) - -(defun score-write-smf (score filename &optional programs as-adagio absolute) - (let ((file (if as-adagio (open filename :direction :output) - (open-binary filename :direction :output))) - (seq (seq-create)) - (chan 1)) - (cond (file - (dolist (program programs) - ;; 6 = SEQ_PROGRAM - (seq-insert-ctrl seq 0 0 6 chan program) - ;(display "insert ctrl" seq 0 0 6 chan program) - (incf chan)) - - (dolist (event (cdr (score-must-have-begin-end score))) - (let ((time (event-time event)) - (dur (event-dur event)) - (chan (event-get-attr event :chan 0)) - (pitch (event-get-attr event :pitch)) - (program (event-get-attr event :program)) - (vel (event-get-attr event :vel 100))) - (cond (program - ;(display "score-write-smf program" chan program) - (seq-insert-ctrl seq (round (* time 1000)) - 0 6 (1+ chan) - (round program)))) - (cond ((consp pitch) - (dolist (p pitch) - (seq-insert-note seq (round (* time 1000)) - 0 (1+ chan) (round p) - (round (* dur 1000)) (round vel)))) - (pitch - (seq-insert-note seq (round (* time 1000)) - 0 (1+ chan) (round pitch) - (round (* dur 1000)) (round vel)))))) - (cond (as-adagio - (seq-write seq file absolute) - (close file)) ;; seq-write does not close file, so do it here - (t - (seq-write-smf seq file))))))) ; seq-write-smf closes file - - - -;; make a default note function for scores -;; -(defun note (&key (pitch 60) (vel 100)) - ;; load the piano if it is not loaded already - (if (not (boundp '*piano-srate*)) - (abs-env (load "piano/pianosyn"))) - (piano-note-2 pitch vel)) - -;;================================================================ - -;; WORKSPACE functions have moved to envelopes.lsp - - -;; DESCRIBE -- add a description to a global variable -;; -(defun describe (symbol &optional description) - (add-to-workspace symbol) - (cond (description - (putprop symbol description 'description)) - (t - (get symbol 'description)))) - -;; INTERPOLATE -- linear interpolation function -;; -;; compute y given x by interpolating between points (x1, y1) and (x2, y2) -(defun interpolate (x x1 y1 x2 y2) - (cond ((= x1 x2) x1) - (t (+ y1 (* (- x x1) (/ (- y2 y1) (- x2 (float x1)))))))) - - -;; INTERSECTION -- set intersection -;; -;; compute the intersection of two lists -(defun intersection (a b) - (let (result) - (dolist (elem a) - (if (member elem b) (push elem result))) - result)) - -;; UNION -- set union -;; -;; compute the union of two lists -(defun union (a b) - (let (result) - (dolist (elem a) - (if (not (member elem result)) (push elem result))) - (dolist (elem b) - (if (not (member elem result)) (push elem result))) - result)) - -;; SET-DIFFERENCE -- set difference -;; -;; compute the set difference between two sets -(defun set-difference (a b) - (remove-if (lambda (elem) (member elem b)) a)) - -;; SUBSETP -- test is list is subset -;; -;; test if a is subset of b -(defun subsetp (a b) - (let ((result t)) - (dolist (elem a) - (cond ((not (member elem b)) - (setf result nil) - (return nil)))) - result)) - -;; functions to support score editing in NyquistIDE - -(if (not (boundp '*default-score-file*)) - (setf *default-score-file* "score.dat")) - -;; SCORE-EDIT -- save a score for editing by NyquistIDE -;; -;; file goes to a data file to be read by NyquistIDE -;; Note that the parameter is a global variable name, not a score, -;; but you do not quote the global variable name, e.g. call -;; (score-edit my-score) -;; -(defmacro score-edit (score-name) - `(score-edit-symbol (quote ,score-name))) - -(defun score-edit-symbol (score-name) - (prog ((f (open *default-score-file* :direction :output)) - score expr) - (cond ((symbolp score-name) - (setf score (eval score-name))) - (t - (error "score-edit expects a symbol naming the score to edit"))) - (cond ((null f) - (format t "score-edit: error in output file ~A!~%" *default-score-file*) - (return nil))) - - (format t "score-edit: writing ~A ...~%" *default-score-file*) - (format f "~A~%" score-name) ; put name on first line - (dolist (event score) ;cdr scor - (format f "~A " (event-time event)) ; print start time - (format f "~A " (event-dur event)) ; print duration - - (setf expr (event-expression event)) - - ; print the pitch and the rest of the attributes - (format f "~A " (expr-get-attr expr :pitch)) - (format f "~A~%" (expr-parameters-remove-attr expr :pitch))) - (close f) - (format t "score-edit: wrote ~A events~%" (length score)))) - - -;; Read in a data file stored in the score-edit format and save -;; it to the global variable it came from -(defun score-restore () - (prog ((inf (open *default-score-file*)) - name start dur pitch expr score) - (cond ((null inf) - (format t "score-restore: could not open ~A~%" *default-score-file*) - (return nil))) - (setf name (read inf)) ;; score name - (loop - (setf start (read inf)) - (cond ((null start) (return))) - (setf dur (read inf)) - (setf pitch (read inf)) - (setf expr (read inf)) - (cond (pitch - (setf expr (expr-set-attr expr :pitch pitch))))) - (close inf) - (setf (symbol-value name) score))) diff --git a/Release/plug-ins/ShelfFilter.ny b/Release/plug-ins/ShelfFilter.ny deleted file mode 100644 index 0fa4e95be501f9a1a76097d182839b93baec1445..0000000000000000000000000000000000000000 --- a/Release/plug-ins/ShelfFilter.ny +++ /dev/null @@ -1,34 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview linear -$name (_ "Shelf Filter") -$debugbutton disabled -$author (_ "Steve Daulton") -$release 2.4.0-1 -$copyright (_ "GNU General Public License v2.0") - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control TYPE (_ "Filter type") choice (("Low" (_ "Low-shelf")) - ("High" (_ "High-shelf"))) 0 -$control HZ (_ "Frequency (Hz)") int "" 1000 10 10000 -$control GAIN (_ "Amount (dB)") int "" -6 -72 72 - - -(cond ((> HZ (/ *sound-srate* 2)) - (format nil (_ "Error.~%Frequency set too high for selected track."))) - ((> HZ (/ *sound-srate* 2.1)) ;Handle edge case close to Nyquist frequency. - (setf *track* (force-srate (* 2 *sound-srate*) *track*)) - (if (= TYPE 0) - (force-srate *sound-srate* (eq-lowshelf *track* HZ GAIN)) - (force-srate *sound-srate* (eq-highshelf *track* HZ GAIN)))) - ((= GAIN 0) "") ; no-op - (t (if (= TYPE 0) - (eq-lowshelf *track* HZ GAIN) - (eq-highshelf *track* HZ GAIN)))) diff --git a/Release/plug-ins/SpectralEditMulti.ny b/Release/plug-ins/SpectralEditMulti.ny deleted file mode 100644 index 6e62a6897a0f74664b8edfbb21c184a428d2afb0..0000000000000000000000000000000000000000 --- a/Release/plug-ins/SpectralEditMulti.ny +++ /dev/null @@ -1,70 +0,0 @@ -$nyquist plug-in -$version 4 -$type process spectral -$name (_ "Spectral Edit Multi Tool") -$author (_ "Paul Licameli") -$release 2.3.0-1 -$copyright (_ "GNU General Public License v2.0") - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - -;; SpectralEditMulti.ny by Paul Licameli, November 2014. -;; Updated by Steve Daulton 2014 / 2015. - - -(defun wet (sig f0 f1 fc) - (cond - ((not f0) (highpass2 sig f1)) - ((not f1) (lowpass2 sig f0)) - (T (let ((q (/ fc (- f1 f0)))) - (notch2 sig fc q))))) - -(defun result (sig) - (let* ((f0 (get '*selection* 'low-hz)) - (f1 (get '*selection* 'high-hz)) - (fc (get '*selection* 'center-hz)) - (bw (get '*selection* 'bandwidth)) - (tn (truncate len)) - (rate (snd-srate sig)) - (transition (truncate (* 0.01 rate))) ; 10 ms - (t1 (min transition (/ tn 2))) ; fade in length (samples) - (t2 (max (- tn transition) (/ tn 2))) ; length before fade out (samples) - (breakpoints (list t1 1.0 t2 1.0 tn)) - (env (snd-pwl 0.0 rate breakpoints))) - (cond - ((not (or f0 f1)) ; This should never happen for a 'spectral' effect. - (throw 'error-message - (format nil (_ "~aPlease select frequencies.") p-err))) - ((and f0 f1 (= f0 f1)) - (throw 'error-message - (format nil (_ "~aBandwidth is zero (the upper and lower~%~ - frequencies are both ~a Hz).~%~ - Please select a frequency range.") - p-err f0))) - ;; Biqud filter fails if centre frequency is very low and bandwidth very high. - ;; 'Magic numbers' 10 Hz and 10 octaves are experimental. - ((and f0 (< f0 10) bw (> bw 10)) - (throw 'error-message - (format nil (_ "~aNotch filter parameters cannot be applied.~%~ - Try increasing the low frequency bound~%~ - or reduce the filter 'Width'.") - p-err))) - ;; low pass frequency is above Nyquist so do nothing - ((and (not f1) (>= f0 (/ *sound-srate* 2.0))) - nil) - ;; notch frequency is above Nyquist so do nothing - ((and f0 f1 (>= fc (/ *sound-srate* 2.0))) - nil) - ;; high-pass above Nyquist so fade to silence - ((and (not f0) (>= f1 (/ *sound-srate* 2.0))) - (mult sig (diff 1.0 env))) - (T (sum (prod env (wet sig f0 f1 fc)) - (prod (diff 1.0 env) sig)))))) - -(catch 'error-message - (setf p-err (format nil (_ "Error.~%"))) - (multichan-expand #'result *track*)) diff --git a/Release/plug-ins/SpectralEditParametricEQ.ny b/Release/plug-ins/SpectralEditParametricEQ.ny deleted file mode 100644 index 8f78788ad1defd0731d70d17a157616b59077216..0000000000000000000000000000000000000000 --- a/Release/plug-ins/SpectralEditParametricEQ.ny +++ /dev/null @@ -1,70 +0,0 @@ -$nyquist plug-in -$version 4 -$type process spectral -$preview linear -$name (_ "Spectral Edit Parametric EQ") -$debugbutton false -$author (_ "Paul Licameli") -$release 2.3.0-2 -$copyright (_ "GNU General Public License v2.0") - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - -;; SpectralEditParametricEQ.ny by Paul Licameli, November 2014. -;; Updated by Steve Daulton 2014 / 2015. - - -$control CONTROL-GAIN (_ "Gain (dB)") real "" 0 -24 24 - -(defun wet (sig gain fc bw) - (eq-band sig fc gain (/ bw 2))) - -(defun result (sig) - (let* - ((f0 (get '*selection* 'low-hz)) - (f1 (get '*selection* 'high-hz)) - (fc (get '*selection* 'center-hz)) - (bw (get '*selection* 'bandwidth)) - (tn (truncate len)) - (rate (snd-srate sig)) - (transition (truncate (* 0.01 rate))) ; 10 ms - (t1 (min transition (/ tn 2))) ; fade in length (samples) - (t2 (max (- tn transition) (/ tn 2))) ; length before fade out (samples) - (breakpoints (list t1 1.0 t2 1.0 tn)) - (env (snd-pwl 0.0 rate breakpoints))) - (cond - ((not (or f0 f1)) ; This should never happen for a 'spectral' effect. - (throw 'error-message (format nil (_ "~aPlease select frequencies.") p-err))) - ((not f0) - (throw 'error-message (format nil (_ "~aLow frequency is undefined.") p-err))) - ((not f1) - (throw 'error-message (format nil (_ "~aHigh frequency is undefined.") p-err))) - ((and fc (= fc 0)) - (throw 'error-message (format nil (_ "~aCenter frequency must be above 0 Hz.") p-err))) - ((and f1 (> f1 (/ *sound-srate* 2))) - (throw 'error-message - (format nil (_ "~aFrequency selection is too high for track sample rate.~%~ - For the current track, the high frequency setting cannot~%~ - be greater than ~a Hz") - p-err (/ *sound-srate* 2)))) - ((and bw (= bw 0)) - (throw 'error-message - (format nil (_ "~aBandwidth is zero (the upper and lower~%~ - frequencies are both ~a Hz).~%~ - Please select a frequency range.") - p-err f0))) - ;; If centre frequency band is above Nyquist, do nothing. - ((and fc (>= fc (/ *sound-srate* 2.0))) - nil) - (t (sum (prod env (wet sig CONTROL-GAIN fc bw)) - (prod (diff 1.0 env) sig)))))) - -(catch 'error-message - (setf p-err (format nil (_ "Error.~%"))) - (if (= CONTROL-GAIN 0) - "" ; No-op - (multichan-expand #'result *track*))) diff --git a/Release/plug-ins/SpectralEditShelves.ny b/Release/plug-ins/SpectralEditShelves.ny deleted file mode 100644 index 9a3135f90ab0201856f8efa83e9569e44672638f..0000000000000000000000000000000000000000 --- a/Release/plug-ins/SpectralEditShelves.ny +++ /dev/null @@ -1,77 +0,0 @@ -$nyquist plug-in -$version 4 -$type process spectral -$preview linear -$name (_ "Spectral Edit Shelves") -$debugbutton false -$author (_ "Paul Licameli") -$release 2.3.0-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - -;; SpectralEditShelves.ny by Paul Licameli, November 2014. -;; Updated by Steve Daulton 2014 / 2015. - - -$control CONTROL-GAIN (_ "Gain (dB)") real "" 0 -24 24 - -(defmacro validate (hz) -"If frequency is above Nyquist, don't use it" - `(if (or (>= ,hz (/ *sound-srate* 2.0)) - (<= ,hz 0)) - (setf ,hz nil))) - -(defun mid-shelf (sig lf hf gain) - "Combines high shelf and low shelf filters" - (let ((invg (- gain))) - (scale (db-to-linear gain) - (eq-highshelf (eq-lowshelf sig lf invg) - hf invg)))) - -(defun wet (sig gain f0 f1) - "Apply appropriate filter" - (cond - ((not f0) (eq-lowshelf sig f1 gain)) - ((not f1) (eq-highshelf sig f0 gain)) - (t (mid-shelf sig f0 f1 gain)))) - -(defun result (sig) - (let* - ((f0 (get '*selection* 'low-hz)) - (f1 (get '*selection* 'high-hz)) - (tn (truncate len)) - (rate (snd-srate sig)) - (transition (truncate (* 0.01 rate))) ; 10 ms - (t1 (min transition (/ tn 2))) ; fade in length (samples) - (t2 (max (- tn transition) (/ tn 2))) ; length before fade out (samples) - (breakpoints (list t1 1.0 t2 1.0 tn)) - (env (snd-pwl 0.0 rate breakpoints))) - (cond - ((not (or f0 f1)) ; This should never happen for a 'spectral' effect. - (throw 'error-message (format nil (_ "~aPlease select frequencies.") p-err))) - ((and f0 (>= f0 (/ *sound-srate* 2.0))) - ; Shelf is above Nyquist frequency so do nothing. - nil) - ((and f0 f1 (= f0 f1)) - (throw 'error-message - (format nil (_ "~aBandwidth is zero (the upper and lower~%~ - frequencies are both ~a Hz).~%~ - Please select a frequency range.") - p-err f0))) - (T (if f0 (validate f0)) - (if f1 (validate f1)) - (if (not (or f0 f1)) ; 'validate' may return nil - nil ; Do nothing - (sum (prod env (wet sig CONTROL-GAIN f0 f1)) - (prod (diff 1.0 env) sig))))))) - -(catch 'error-message - (setf p-err (format nil (_ "Error.~%"))) - (if (= CONTROL-GAIN 0) - "" ; No-op - (multichan-expand #'result *track*))) diff --git a/Release/plug-ins/StudioFadeOut.ny b/Release/plug-ins/StudioFadeOut.ny deleted file mode 100644 index b4812d9b8e36e3c6ab1a04993fd463d1c30efa8d..0000000000000000000000000000000000000000 --- a/Release/plug-ins/StudioFadeOut.ny +++ /dev/null @@ -1,43 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$name (_ "Studio Fade Out") -$author (_ "Steve Daulton") -$release 3.0.4-1 -$copyright (_ "GNU General Public License v2.0 or later") - -;; Produce a smooth and musical sounding fade out. -;; Applies a sinusoidal fade out with a progressive low-pass -;; filter from full spectrum at start to 100 Hz at end. - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -;;; sweeping low pass filter - (defun filter (sig dur) - (abs-env - ;; cross-fade the filter - (let* ((nyq-hz (/ *sound-srate* 2)) - (f-out (r-cos (min (/ dur 2.0) 0.5))) - (f-in (diff (snd-const 1 0 *sound-srate* dur) f-out))) - (sim - (mult f-out sig) - (mult f-in (lp sig (pwlv nyq-hz dur 100))))))) - -;;; raised cosine -(defun r-cos (dur) - (abs-env - (mult 0.5 - (sum 1 - (osc (hz-to-step (/ (* dur 2))) dur *table* 90))))) - -(let ((dur (get-duration 1))) - (cond - ((< len 3) (format nil (_ "Selection too short.~%It must be more than 2 samples."))) - ((< dur 0.2) (mult *track* (r-cos dur))) - (t (mult (filter *track* dur)(r-cos dur))))) - diff --git a/Release/plug-ins/adjustable-fade.ny b/Release/plug-ins/adjustable-fade.ny deleted file mode 100644 index 4ee7bbe355c8c883f5034393fdbe684f14c23412..0000000000000000000000000000000000000000 --- a/Release/plug-ins/adjustable-fade.ny +++ /dev/null @@ -1,211 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview linear -$preview selection -$name (_ "Adjustable Fade") -$debugbutton false -$author (_ "Steve Daulton") -$release 3.0.4-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control TYPE (_ "Fade Type") choice (("Up" (_ "Fade Up")) - ("Down" (_ "Fade Down")) - ("SCurveUp" (_ "S-Curve Up")) - ("SCurveDown" (_ "S-Curve Down"))) 0 -$control CURVE (_ "Mid-fade Adjust (%)") real "" 0 -100 100 -$control UNITS (_ "Start/End as") choice (("Percent" (_ "% of Original")) - ("dB" (_ "dB Gain"))) 0 -$control GAIN0 (_ "Start (or end)") float-text "" 0 nil nil -$control GAIN1 (_ "End (or start)") float-text "" 100 nil nil -$control PRESET (_ "Handy Presets (override controls)") choice (("None" (_ "None Selected")) - ("LinearIn" (_ "Linear In")) - ("LinearOut" (_ "Linear Out")) - ("ExponentialIn" (_ "Exponential In")) - ("ExponentialOut" (_ "Exponential Out")) - ("LogarithmicIn" (_ "Logarithmic In")) - ("LogarithmicOut" (_ "Logarithmic Out")) - ("RoundedIn" (_ "Rounded In")) - ("RoundedOut" (_ "Rounded Out")) - ("CosineIn" (_ "Cosine In")) - ("CosineOut" (_ "Cosine Out")) - ("SCurveIn" (_ "S-Curve In")) - ("SCurveOut" (_ "S-Curve Out"))) 0 - -;;; Preview takes the entire selection so that we know the correct -;;; selection length, but preview only needs to process preview length." -(defun get-input (sig) - (if *previewp* - (multichan-expand #'trim-input sig) - sig)) - - -;;; Trim input when previewing." -(defun trim-input (sig) - (let ((dur (min (get-duration 1) - (get '*project* 'preview-duration)))) - (setf sig (extract-abs 0 dur sig)))) - - -;;; Check gain values are in valid range. -(defun validate-gain () - (setf err (format nil (_ "Error~%~%"))) - (if (= UNITS 0) ;percentage values - (cond - ((or (< GAIN0 0)(< GAIN1 0)) - (throw 'err (format nil (_ "~aPercentage values cannot be negative.") err))) - ((or (> GAIN0 1000)(> GAIN1 1000)) - (throw 'err (format nil (_ "~aPercentage values cannot be more than 1000 %.") err)))) - (cond ;dB values - ((or (> GAIN0 100)(> GAIN1 100)) - (throw 'err (format nil (_ "~adB values cannot be more than +100 dB.~%~%~ - Hint: 6 dB doubles the amplitude~%~ - -6 dB halves the amplitude.") err)))))) - - -;;; Select and apply fade -(defun fade (sig) - (when (= PRESET 0) - ; Can't use widget validation for gain as range depends on units. - (validate-gain)) - (psetq curve-ratio (/ CURVE 100.0) - g0 (gainscale GAIN0) - g1 (gainscale GAIN1)) - (mult (get-input sig) - (case PRESET - (0 (case TYPE ; Custom fade - (0 (simple (min g0 g1) (max g0 g1) curve-ratio)) - (1 (simple (max g0 g1) (min g0 g1) curve-ratio)) - (2 (raised-cos (min g0 g1)(max g0 g1) curve-ratio)) - (T (raised-cos (max g0 g1) (min g0 g1) curve-ratio)))) - (1 (linear 0 1)) ; Linear In - (2 (linear 1 0)) ; Linear Out - (3 (log-exp-curve -60 0)) ; Exponential In - (4 (log-exp-curve -60 1)) ; ExponentialOut - (5 (log-exp-curve 15.311 0)) ; Logarithmic In - (6 (log-exp-curve 15.311 1)) ; Logarithmic Out - (7 (simple-curve 0 1 0.5)) ; Rounded In - (8 (simple-curve 1 0 0.5)) ; Rounded Out - (9 (cosine-curve 0 1)) ; Cosine In - (10 (cosine-curve 1 0)) ; Cosine Out - (11 (raised-cos 0 1 0.0)) ; S-Curve In - (t (raised-cos 1 0 0.0))))) ; S-Curve Out - - -;;; Simple Curve: -;;; Use cosine for + values and linear for -ve. -(defun simple (g0 g1 curve-ratio) - (cond - ((= g0 g1) g0) ; amplify - ((and (> curve-ratio 0)(< curve-ratio 0.5)) ; +ve curve less than 0.5, lin to cosine - (let ((curve-ratio (* curve-ratio 2))) - (sim (mult (scale-curve g0 g1 (linear g0 g1)) - (- 1 curve-ratio)) ; linear - (mult (scale-curve g0 g1 (cosine-curve g0 g1)) - curve-ratio)))) ; cosine curve - ((> curve-ratio 0) - (cos-curve g0 g1 (- 1.5 curve-ratio))) ; +ve curve > 0.5 - (t (simple-curve g0 g1 (- 1 (* 2 curve-ratio)))))) ; -ve curve - - -;;; Linear fade to the power of 'pow'. -(defun simple-curve (g0 g1 pow) - (curve-adjust g0 g1 pow (linear g0 g1))) - - -;;; Cosine fade to the power of 'pow'. -(defun cos-curve (g0 g1 pow) - (curve-adjust g0 g1 pow (cosine-curve g0 g1))) - - -(defun curve-adjust (g0 g1 pow env) - (scale-curve g0 g1 - (if (= pow 1) - env - (snd-exp (mult pow (snd-log env)))))) - - -;;; Scale curves to min, max. -(defun scale-curve (g0 g1 env) - (sum (min g0 g1) - (mult (abs (- g0 g1)) env))) - - -;;; Cosine curve. -(defun cosine-curve (g0 g1) - (let ((step (hz-to-step (/ 0.25 (get-duration 1)))) - (phase (if (> g0 g1) 90 0))) - (osc step 1 *sine-table* phase))) - - -;;; Linear fade in, out. -(defun linear (g0 g1) - (control-srate-abs *sound-srate* - (if (> g0 g1) ; g0 = g1 does not occur here. - (pwlv 1 1 0) ; fade out - (pwlv 0 1 1)))) ; else fade in - - -;;; Raised cosine fades. -(defun raised-cos (g0 g1 curve-ratio) - (setq curve-ratio - (if (> curve-ratio 0) - (exp-scale-mid (* 2 curve-ratio)) ; mid-point -3dB @ Adjust = 50% - (exp-scale-mid (* 1.63 curve-ratio)))) ; mid-point -12dB @ Adjust = -50% - (setf env - ;; sound srate required for accuracy. - (control-srate-abs *sound-srate* - (cond - ((= g0 g1) g0) ; amplify - ((> g0 g1) ; fade down - (snd-exp - (mult (pwlv (- 1 curve-ratio) 1 1) - (snd-log (raised-cosin 90))))) - (t (snd-exp ; fade up - (mult (pwlv 1 1 (- 1 curve-ratio)) - (snd-log (raised-cosin -90)))))))) - (sum (min g0 g1) - (mult (abs (- g0 g1)) env))) - - -;;; Raised cosine curve. -(defun raised-cosin (phase) - (let ((hz (hz-to-step (/ (get-duration 2))))) - (mult 0.5 - (sum 1 (osc hz 1 *sine-table* phase))))) - - -;;; log or exponential curve scaled 0 to 1 -;;; x is the minimum level in dB before scaling. -(defun log-exp-curve (x direction) - (control-srate-abs *sound-srate* - (let ((x (db-to-linear x))) - ;; If direction=0 fade-in else fade-out - (if (= direction 0) - (setf env (pwev x 1 1)) - (setf env (pwev 1 1 x))) - (mult (/ (- 1 x)) ; normalize to 0 dB - (diff env x))))) ; drop down to silence - - -;;; Curve scaling for S-curve. -(defun exp-scale-mid (x) - (let ((e (exp 1.0))) - (/ (- (exp (- 1 x)) e) - (- 1 e)))) - - -(defun gainscale (gain) - (if (= UNITS 0) ; percent - (/ gain 100.0) - (db-to-linear gain))) - - -(catch 'err (fade *track*)) diff --git a/Release/plug-ins/beat.ny b/Release/plug-ins/beat.ny deleted file mode 100644 index 208f7134722131bd7129829211f1c5ec0f5cfe66..0000000000000000000000000000000000000000 --- a/Release/plug-ins/beat.ny +++ /dev/null @@ -1,43 +0,0 @@ -$nyquist plug-in -$version 4 -$type analyze -$name (_ "Beat Finder") -$debugbutton false -$author (_ "Audacity") -$release 2.3.2-2 -$copyright (_ "GNU General Public License v2.0") - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control THRESVAL (_ "Threshold Percentage") int "" 65 5 100 - -(setf threshold (/ THRESVAL 100.0)) - -(defun mix-to-mono (sig) - (if (arrayp sig) - (sum (aref sig 0) (aref sig 1)) - sig)) - -(defun bass-tracker (sig) - (let* ((bass (lp sig 50)) - ;(snd-follow sound floor risetime falltime lookahead) - (follower (snd-follow bass 0.001 0.01 0.1 512))) - (force-srate 1000 (lp follower 10)))) - - -(let ((beats (bass-tracker (mix-to-mono *track*)))) - (setf peak-sig (peak beats ny:all)) - (setf threshold (* threshold peak-sig)) - (do ((time 0.0 (+ time 0.001)) - (val (snd-fetch beats) (snd-fetch beats)) - (flag T) - labels) - ((not val) labels) - (when (and flag (> val threshold)) - (push (list time "B") labels)) - (setf flag (< val threshold)))) diff --git a/Release/plug-ins/clipfix.ny b/Release/plug-ins/clipfix.ny deleted file mode 100644 index d2edf3b8a4709936ae81d31fe874e4096387fdc0..0000000000000000000000000000000000000000 --- a/Release/plug-ins/clipfix.ny +++ /dev/null @@ -1,108 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview enabled -$name (_ "Clip Fix") -$debugbutton false -$author (_ "Benjamin Schwartz and Steve Daulton") -$release 2.3.0-2 -$copyright (_ "GNU General Public License v2.0") - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - -;; Algorithm by Benjamin Schwartz -;; Clip Fix is a simple, stupid (but not blind) digital-clipping-corrector -;; The algorithm is fairly simple: -;; 1. Find all clipped regions -;; 2. Get the slope immediately on either side of the region -;; 3. Do a cubic spline interpolation. -;; 4. Go to next region - - -$control THRESHOLD (_ "Threshold of Clipping (%)") float "" 95 0 100 -$control GAIN (_ "Reduce amplitude to allow for restored peaks (dB)") float "" -9 -30 0 - -(setf thresh-ratio (/ THRESHOLD 100)) -(setf gain-lin (db-to-linear GAIN)) -(setf buffersize 100000) -(setf slopelength 4) ; number of samples used to calculate the exit / re-entry slope - - -(defun declip (sig thresh peak) - (let* ((thresh (* thresh peak)) - (ln (truncate len)) - (finalbufsize (rem ln buffersize))) - ;; Calculate the number of buffers we can process. - ;; if final buffer is not large enough for de-clipping we - ;; will just add it on the end as is. - (if (>= finalbufsize slopelength) - (setf buffercount (1+ (/ ln buffersize))) - (setf buffercount (/ ln buffersize))) - ;;; Make output sequence from processed buffers - (setf out - (seqrep (i buffercount) - (let* ((step (min buffersize (- ln (* i buffersize)))) - (buffer (snd-fetch-array sig step step)) - (processed (process buffer thresh step))) - (cue (mult gain-lin - (snd-from-array 0 *sound-srate* processed)))))) - ;;; If there's unprocessed audio remaining, add it to the end - (if (and (> finalbufsize 0)(< finalbufsize slopelength)) - (seq out (cue (getfinalblock sig finalbufsize gain-lin))) - out))) - - -(defun getfinalblock (sig step gain-lin) - (let ((block (snd-fetch-array sig step step))) - (mult gain-lin (snd-from-array 0 *sound-srate* block)))) - - -(defun process (buffer thresh bufferlength) - ;;; Find threshold crossings - (setf exit-list ()) ; list of times when waveform exceeds threshold - (setf return-list ()) ; list of times when waveform returns below threshold - ;; Limitation of algorithm: the first and last 'slopelength' at ends of buffer are ignored - ;; so that we have enough samples beyond the threshold crossing to calculate the slope. - (let ((last-sample (- bufferlength slopelength))) - (do ((i slopelength (1+ i))) - ((>= i last-sample)) - (if (>= (abs (aref buffer i)) thresh) - (when (< (abs (aref buffer (- i 1))) thresh) ; we just crossed threshold - (push (- i 1) exit-list)) - (when (>= (abs (aref buffer (- i 1))) thresh) ; we just got back in range - (push i return-list))))) - ;; Reverse lists back into chronological order. - ;; This is faster than appending values in chronological order. - (setf exit-list (reverse exit-list)) - (setf return-list (reverse return-list)) - ;; If the audio begins in a clipped region, discard the first return - (when (>= (abs (aref buffer (1- slopelength))) thresh) - (setq return-list (cdr return-list))) - ;; Interpolate between each pair of exit / entry points - (let ((slopelen (1- slopelength))) - (mapc (lambda (t0 t1) - (interpolate buffer t0 t1 slopelen)) - exit-list return-list)) - buffer) - - -(defun interpolate (buffer t0 t1 dur) - "Cubic spline interpolation" - (let* ((d0 (/ (- (aref buffer t0) (aref buffer (- t0 dur))) dur)) ; slope at start - (d1 (/ (- (aref buffer (+ t1 dur)) (aref buffer t1)) dur)) ; slope at end - (m (/ (+ d1 d0) (* (- t1 t0) (- t1 t0)))) - (b (- (/ d1 (- t1 t0)) (* m t1)))) - (do ((j (1+ t0) (1+ j))) - ((= j t1)) - (setf (aref buffer j) - (+ (* (- t1 j) (/ (aref buffer t0) (- t1 t0))) - (* (- j t0) (/ (aref buffer t1) (- t1 t0))) - (* (- j t0) (- j t1) (+ (* m j) b))))))) - - -;; (get '*selection* 'peak) introduced in Audacity 2.1.3 -(multichan-expand #'declip *track* thresh-ratio (get '*selection* 'peak)) diff --git a/Release/plug-ins/crossfadeclips.ny b/Release/plug-ins/crossfadeclips.ny deleted file mode 100644 index e8bbe8ccaf839469dd25f6b9d93945ff8ba96290..0000000000000000000000000000000000000000 --- a/Release/plug-ins/crossfadeclips.ny +++ /dev/null @@ -1,132 +0,0 @@ -$nyquist plugin -$version 4 -$type process -$mergeclips 1 -$restoresplits 0 -$name (_ "Crossfade Clips") -$author (_ "Steve Daulton") -$release 3.0.4-1 -$copyright (_ "GNU General Public License v2.0 or later") - - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - -;; Instructions: -;; Place two audio clips into the same track. -;; Select (approximately) the same amount of audio from the -;; end of one clip and the start of the other. -;; Apply the effect. -;; The selected regions will be crossfaded. -;; -;; Note, the audio clips do not need to be touching. Any -;; white-space between the clips is ignored. -;; -;; If the selected region is continuous audio (no splits), -;; the the first and last halves of the selected audio -;; will be crossfaded. -;; -;; Advanced Tip: -;; A discontinuity in a waveform may be smoothed by applying -;; a short crossfade across the glitch. - -;; Limitations (should not occur in normal usage). -;; 1) There may be no more than two clips selected in each channel. -;; 2) The selection may not start or end in white-space. - - -(setf err1 (format nil (_ "Error.~%Invalid selection.~%More than 2 audio clips selected."))) -(setf err2 (format nil (_ "Error.~%Invalid selection.~%Empty space at start/ end of the selection."))) - - -(defun find-ends (T0 T1 clips) -"Look for a split or gap within the selection, or return the mid-point" - (let ((trk-ends ()) ;starts of clips - (trk-starts ())) ;ends of clips - (dolist (clip clips) - ;; look for clip enclosing the selection. - (when (and (>= (second clip) T1) (<= (first clip) T0)) - (psetq trk-ends (list (/ (+ T0 T1) 2)) - trk-starts (list (/ (+ T0 T1) 2))) - (return)) - ;; look for track starts. - (when (and (> (first clip) T0) (< (first clip) T1)) - (push (first clip) trk-starts)) - ;; look for track ends. - (when (and (> (second clip) T0) (< (second clip) T1)) - (push (second clip) trk-ends)) - ; stop looking when we have passed end of selection. - (when (> (first clip) T1) (return))) - ;; if exactly one split position for crossfading, - ;; return clip positions, else error. - (cond - ((and (= (length trk-ends) 1) - (= (length trk-starts) 1) - (<= (car trk-ends) (car trk-starts))) - (list (car trk-ends)(car trk-starts))) - ((or (> (length trk-ends) 1) - (> (length trk-starts) 1)) - (throw 'error err1)) - (T (throw 'error err2))))) - -(defun crossfade (sig out-end in-start end) -"Do the crossfade" - (abs-env - (control-srate-abs *sound-srate* - (let* ((fade-out (mult sig (env out-end 0))) - (cflen (max out-end (- end in-start))) ;crossfade length - (finstart (max (- out-end (- end in-start)) 0)) - (fade-in (mult (extract (- end cflen) end sig) - (env (- cflen finstart) 1 finstart)))) - (sim fade-out fade-in))))) - -(defun env (dur direction &optional (offset 0)) -"Generate envelope for crossfade" - (abs-env - (if (< dur 0.01) ;make it linear - (control-srate-abs *sound-srate* - (if (= direction 0) - (pwlv 1 dur 0) ;fade out - (pwlv 0 offset 0 (+ offset dur) 1))) ;fade in - (if (= direction 0) ;cosine curve - (cos-curve dur 0) - (seq (s-rest offset) - (cos-curve dur 1)))))) - -(defun cos-curve (dur direction) -"Generate cosine curve" - (if (= direction 0) ;fade out - (osc (hz-to-step (/ 0.25 dur)) dur *sine-table* 90) - (osc (hz-to-step (/ 0.25 dur)) dur *sine-table* 0))) - -(defun process (sig t0 t1 clips) -"Find the split positions and crossfade" - (setf fadeclips - (multichan-expand #'find-ends t0 t1 clips)) - (if (arrayp fadeclips) - (prog ((fade-out-end (min (first (aref fadeclips 0)) - (first (aref fadeclips 1)))) - (fade-in-start (max (second (aref fadeclips 0)) - (second (aref fadeclips 1))))) - (return - (multichan-expand #'crossfade sig - (- fade-out-end t0) - (- fade-in-start t0) - (- t1 t0)))) - (crossfade sig - (- (first fadeclips) t0) - (- (second fadeclips) t0) - (- t1 t0)))) - - -;;; Run the program. -(if (= (length (get '*selection* 'tracks)) 1) - (catch 'error - (process *track* - (get '*selection* 'start) - (get '*selection* 'end) - (get '*track* 'clips))) - (format nil (_ "Error.~%Crossfade Clips may only be applied to one track."))) diff --git a/Release/plug-ins/crossfadetracks.ny b/Release/plug-ins/crossfadetracks.ny deleted file mode 100644 index 8ca26be32f1636b066f50277d5612220bbdb11f1..0000000000000000000000000000000000000000 --- a/Release/plug-ins/crossfadetracks.ny +++ /dev/null @@ -1,85 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$name (_ "Crossfade Tracks") -$debugbutton disabled -$preview selection -$author (_ "Steve Daulton") -$release 3.0.4-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - -$control TYPE (_ "Fade TYPE") choice ( - ("ConstantGain" (_ "Constant Gain")) - ("ConstantPower1" (_ "Constant Power 1")) - ("ConstantPower2" (_ "Constant Power 2")) - ("CustomCurve" (_ "Custom Curve"))) 0 -$control CURVE (_ "Custom curve") real "" 0 0 1 -$control DIRECTION (_ "Fade direction") choice ( - (_ "Automatic") - ("OutIn" (_ "Alternating Out / In")) - ("InOut" (_ "Alternating In / Out"))) 0 - - -(defun crossfade () - (setf fade-out - (case DIRECTION - (0 (equal (guessdirection) 'OUT)) ; auto - (1 (oddp (get '*track* 'index))) ; fade out odd - (T (evenp (get '*track* 'index))))) ; fade out even - ; Set control rate to sound rate to ensure length is exact. - (setf *control-srate* *sound-srate*) - (mult *track* - (cond - (fade-out - (case TYPE - (0 (pwlv 1 1 0)) - (1 (osc (hz-to-step (/ (get-duration 4))) 1 *sine-table* 90)) - (2 (s-sqrt (pwlv 1 1 0))) - (T (custom 0)))) - (T ; else fade in. - (case TYPE - (0 (pwlv 0 1 1)) - (1 (osc (hz-to-step (/ (get-duration 4))) 1)) - (2 (s-sqrt (pwlv 0 1 1))) - (T (custom 1))))))) - -(defun custom (inout) - ;; 'epsilon' defines the curvature of a logarithmc curve. - ;; To avoid log 0 or /0 it must be > 0 and < 1. - (let* ((ccurve (+ 0.99 (* -0.98 CURVE))) - ; magic number 2.7 gives approx 'constant power' curve at 50% setting. - (epsilon (power ccurve 2.7))) - (if (= inout 0) - (setf logcurve (pwev epsilon 1 1)) - (setf logcurve (pwev 1 1 epsilon))) - ; Scale and invert curve for 0 to unity gain. - (sum 1 - (mult (/ -1 (- 1 epsilon)) - (diff logcurve epsilon))))) - -(defun guessdirection () - ;;; If the selection is closer to the start of the - ;;; audio clip, fade in, otherwise fade out. - ;;; Use `inclips`, i.e., the clip boundaries before the stretch-rendering pre-processing step. - (let* ((start (get '*selection* 'start)) - (end (get '*selection* 'end)) - (clips (get '*track* 'inclips)) - (in-dist end) - (out-dist end)) - (if (arrayp clips) - (setf clips (append (aref clips 0)(aref clips 1)))) - (dotimes (i (length clips)) - (setf in-dist (min in-dist (abs (- start (first (nth i clips)))))) - (setf out-dist (min out-dist (abs (- end (second (nth i clips))))))) - (if (< in-dist out-dist) 'in 'out))) - - -(if (< (length (get '*selection* 'tracks)) 2) - (format nil (_ "Error.~%Select 2 (or more) tracks to crossfade.")) - (crossfade)) diff --git a/Release/plug-ins/delay.ny b/Release/plug-ins/delay.ny deleted file mode 100644 index 94bda9db1c72a865d02bdb9de8e238b8cb3a8903..0000000000000000000000000000000000000000 --- a/Release/plug-ins/delay.ny +++ /dev/null @@ -1,139 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview linear -$name (_ "Delay") -$debugbutton false -$author (_ "Steve Daulton") -$release 2.4.2-2 -$copyright (_ "GNU General Public License v2.0") - - -;; License: GPL v2 or later. -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; based on 'Delay' by David R. Sky -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control DELAY-TYPE (_ "Delay type") choice ((_ "Regular") - ("BouncingBall" (_ "Bouncing Ball")) - ("ReverseBouncingBall" (_ "Reverse Bouncing Ball"))) 0 -$control DGAIN (_ "Delay level per echo (dB)") real "" -6 -30 1 -$control DELAY (_ "Delay time (seconds)") real "" 0.3 0 5 -$control PITCH-TYPE (_ "Pitch change effect") choice (("PitchTempo" (_ "Pitch/Tempo")) - ("LQPitchShift" (_ "Low-quality Pitch Shift")) - ("HQPitchShift" (_ "High-quality Pitch Shift"))) 0 -$control SHIFT (_ "Pitch change per echo (semitones)") real "" 0 -2 2 -$control NUMBER (_ "Number of echoes") int "" 5 1 30 -$control CONSTRAIN (_ "Allow duration to change") choice ((_ "Yes")(_ "No")) 0 - - -;; High-quality Pitch Shift option added, March 2023. -;; -;; "High-quality Pitch Shift" is accomplished with a phase vocoder. -;; "Pitch/Tempo" and "Low-quality Pitch Shift" remain identical -;; to previous version of Audacity. -;; -;; "Pitch/Tempo" is simple resampling, so both pitch and tempo -;; of the delayed audio will change (as in Audacity's -;; "Change Speed" effect). -;; -;; "Low-quality Pitch Shift" changes the pitch without changing -;; the tempo, but has relatively poor sound quality. - - -;;; Pitch shift audio. -(defun p-shift (sig snd-len ratio) - (when (= SHIFT 0) - ; no-op. - (return-from p-shift sig)) - (case PITCH-TYPE (0 (change-speed sig ratio)) - (1 (lq-pitch sig ratio)) - (t (hq-pitch sig snd-len ratio)))) - - -;;; Change speed. -(defun change-speed (sig ratio) - (force-srate *sound-srate* - (stretch-abs (/ ratio) (sound sig)))) - - -;;; Low quality pitch shift. -;; This uses the ancient "Synthesis Toolkit" pitch shifter. -;; STK_PITSHIFT: a simple pitch shifter using delay lines. -;; Filtering and fixed sample rate are used to squeeze slightly -;; better sound quality out of this old library. -(defun lq-pitch(sig ratio) - ; pitshift quality best at 44100 - (let ((sig (force-srate 44100 sig)) - ; anti-alias filter frequency - (minrate (* 0.5 (min *sound-srate* 44100)))) - (force-srate *sound-srate* - ; pitshift requires rates to match - (progv '(*sound-srate*) (list 44100) - (cond - ((> SHIFT 5) ; reduce aliasing - (pitshift (lp-wall sig (/ minrate ratio)) ratio 1)) - ((< SHIFT -2) ; reduce sub-sonic frequencies - (pitshift (hp sig 20) ratio 1)) - (T (pitshift sig ratio 1))))))) - - -;;; Anti-alias low pass filter -(defun lp-wall (sig freq) - (do ((count 0 (1+ count)) - (freq (* 0.94 freq))) - ((= count 10) sig) - (setf sig (lowpass8 sig freq)))) - - -;;; High quality pitch shift. -(defun hq-pitch(sig snd-len shift-ratio) - (let ((stretchfn (const 1)) - (pitchfn (const shift-ratio))) - (pv-time-pitch sig stretchfn pitchfn snd-len))) - - -;;; Apply effects to echo -(defun modify (sig echo-num snd-len) - (let ((gain (db-to-linear (* echo-num DGAIN))) - ; convert semitone shift to ratio. - (ratio (power 2.0 (/ (* echo-num SHIFT) 12.0)))) - (if (= PITCH-TYPE 0) - (mult gain (change-speed sig ratio)) - (mult gain (p-shift sig snd-len ratio))))) - - -;;; Compute echoes. -(defun delays (sig snd-len) - (if (>= DELAY-TYPE 1) ; Bouncing delay. - (setf time-shift (/ DELAY NUMBER)) - (setf time-shift DELAY)) - ;; The echo loop. - (let ((echo (s-rest 0))) - (do ((count 1 (1+ count)) - (dly 0)) - ((> count NUMBER)(sim echo sig)) - (let ((modified-sig (modify sig count snd-len))) - (setq dly - (case DELAY-TYPE - (0 (+ dly time-shift)) - (1 (+ dly (* time-shift (- (1+ NUMBER) count)))) - (2 (+ dly (* time-shift count))))) - (setf echo (sim - (at 0 (cue echo)) - (at-abs dly - (cue modified-sig)))))))) - - -(defun constrain-abs (sig dur) - (extract-abs 0 dur (cue sig))) - - -(let* ((dur (get-duration 1)) - (output (multichan-expand #'delays *track* dur))) - (if (= CONSTRAIN 1) - (multichan-expand #'constrain-abs output dur) - output)) diff --git a/Release/plug-ins/equalabel.ny b/Release/plug-ins/equalabel.ny deleted file mode 100644 index 7e98b80a27c24eee1b571032ef0267029bc13895..0000000000000000000000000000000000000000 --- a/Release/plug-ins/equalabel.ny +++ /dev/null @@ -1,167 +0,0 @@ -$nyquist plug-in -$version 4 -$type tool analyze -$debugbutton false -$debugflags trace -$name (_ "Regular Interval Labels") -$author (_ "Steve Daulton") -$release 2.3.1-2 -$copyright (_ "GNU General Public License v2.0 or later") - - -;; Original version by David R. Sky (http://www.garyallendj.com/davidsky/) 2007. -;; Based on an idea by Sami Jumppanen, with contributions from -;; Alex S.Brown, Dominic Mazzoni, Pierre M.I., Gale Andrews. -;; -;; TODO: Rewrite as an AUD-DO script so as to remove the requirement for -;; an audio selection. - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -; i18n-hint: Refers to the controls 'Number of labels' and 'Label interval'. -$control MODE (_ "Create labels based on") choice (("Both" (_ "Number and Interval")) - ("Number" (_ "Number of Labels")) - ("Interval" (_ "Label Interval"))) 0 -$control TOTALNUM (_ "Number of labels") int-text "" 10 1 1000 -$control INTERVAL (_ "Label interval (seconds)") float-text "" 10 0.001 3600 -$control REGION (_ "Length of label region (seconds)") float-text "" 0 0 3600 -$control ADJUST (_ "Adjust label interval to fit length") choice ((_ "No") - (_ "Yes")) 0 -$control LABELTEXT (_ "Label text") string "" (_ "Label") -$control ZEROS (_ "Minimum number of digits in label") choice (("TextOnly" (_ "None - Text Only")) - ("OneBefore" (_ "1 (Before Label)")) - ("TwoBefore" (_ "2 (Before Label)")) - ("ThreeBefore" (_ "3 (Before Label)")) - ("OneAfter" (_ "1 (After Label)")) - ("TwoAfter" (_ "2 (After Label)")) - ("ThreeAfter" (_ "3 (After Label)"))) 2 -$control FIRSTNUM (_ "Begin numbering from") int-text "" 1 0 nil -$control VERBOSE (_ "Message on completion") choice ((_ "Details") - ("Warnings" (_ "Warnings only")) - (_ "None")) 0 - - -(defun make-labels (num-txt zeropad &aux labels) -"Generate labels at regular intervals" - ;; Get parameters - (case MODE - (1 ; Based on Number - (setf intervals - (if (= REGION 0) - (/ (get-safe-duration) TOTALNUM) - (/ (get-safe-duration) (1- TOTALNUM)))) - (setf total TOTALNUM)) - (2 ; Based on Interval - (setf total (get-interval-count)) - (if (= ADJUST 1) - (setf intervals (/ (get-safe-duration) total)) - (setf intervals INTERVAL))) - (t ; Number and Interval - (psetq total TOTALNUM - intervals INTERVAL))) - ;; Loop for required number of labels - (do* ((count 0 (1+ count)) - (time 0 (* count intervals))) - ((= count total)) - (push (make-one-label time (+ FIRSTNUM count) num-txt zeropad) - labels)) - - (when (and (> REGION 0)(= MODE 2)(= ADJUST 1)) - (push (make-one-label (get-safe-duration) - (+ FIRSTNUM total) - num-txt - zeropad) - labels)) - ;; Create confirmation message - (when (< VERBOSE 2) - (message total intervals)) - labels) - - -(defun message (number intervals) -"Generate output message in debug window." - (if (= number 0) - (setf msg (format nil( _ "Error: There is insufficient space to create labels.~%"))) - (if (> REGION intervals) - (setf msg (format nil (_ "Warning: Overlapping region labels.~%"))) - (setf msg ""))) - (cond - ((= VERBOSE 1) ; Warnings only - (format t msg)) - (t (if (> REGION 0) - ; i18n-hint: Type of label - (setf labeltype (_ "region labels")) - (setf labeltype (_ "point labels"))) - (when (and (> REGION 0)(= MODE 2)(= ADJUST 1)) - (setf number (1+ number))) - (setf msg - ; i18n-hint: Number of labels produced at specified intervals. - (format nil (_ "~a~a ~a at intervals of ~a seconds.~%") - msg number labeltype intervals)) - (if (> REGION 0) - (format t (_ "~aRegion length = ~a seconds.") - msg REGION) - (format t msg))))) - - -(defun get-interval-count (&aux dur) -"Number of labels when interval is specified" - (setf dur (get-safe-duration)) - (case ADJUST - ;; Interval is user input value - (0 (let ((n (truncate (/ dur INTERVAL)))) - (if (< (* n INTERVAL) dur) - (1+ n) - n))) - ;; Adjust space between labels to fit length - (1 (let* ((min-num (truncate (/ dur INTERVAL))) - (max-num (1+ min-num))) - (if (and (> min-num 0) - (< (abs (- INTERVAL (/ dur min-num))) - (abs (- INTERVAL (/ dur max-num))))) - min-num - max-num))))) - - -(defun make-one-label (time num num-txt zeropad) -"Make a single label" - (let* ((num-text (format nil "~a" num)) - (non-zero-digits (length num-text))) - (if (= zeropad 0) - (setf num-text "") - (dotimes (i (max 0 (- zeropad non-zero-digits))) - (setf num-text (format nil "~a~a" "0" num-text)))) - (if num-txt ; Number before text in label. - (setf text (format nil "~a~a" num-text LABELTEXT)) - (setf text (format nil "~a~a" LABELTEXT num-text))) - (list time (+ time REGION) text))) - - -(defun lasttrackp () -"True when processing the final selected track" - (let ((index (get '*track* 'index)) - (num (length (get '*selection* 'tracks)))) - (= index num))) - - -(defun get-safe-duration () - "Returns a safe duration for the labels to be distributed in" - (let ((duration (- (get-duration 1) REGION))) - (if (< duration 0) - 0 - duration))) - - -(setf num-before-text (<= ZEROS 3)) -(setf zeropad (1+ (rem (1- ZEROS) 3))) - -;; Analyze plug-ins may return text message per track but -;; we only want error messages once, and only one set of labels. -(if (lasttrackp) - (make-labels num-before-text zeropad) - "") ; No-op diff --git a/Release/plug-ins/highpass.ny b/Release/plug-ins/highpass.ny deleted file mode 100644 index 466c945230736f6accd1b8bc78f8ab7b6b7de4a1..0000000000000000000000000000000000000000 --- a/Release/plug-ins/highpass.ny +++ /dev/null @@ -1,40 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview linear -$name (_ "High-Pass Filter") -$debugbutton disabled -$author (_ "Dominic Mazzoni") -$release 2.3.0-2 -$copyright (_ "GNU General Public License v2.0") - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control FREQUENCY (_ "Frequency (Hz)") float-text "" 1000 0 nil -$control ROLLOFF (_ "Roll-off (dB per octave)") choice (("dB6" (_ "6 dB")) - ("dB12" (_ "12 dB")) - ("dB24" (_ "24 dB")) - ("dB36" (_ "36 dB")) - ("dB48" (_ "48 dB"))) 0 - - -(cond - ; Validate minimum frequency at run time so we can give a - ; less cryptic error message than built-in widget validation. - ((< FREQUENCY 0.1) - (_ "Frequency must be at least 0.1 Hz.")) - ((>= FREQUENCY (/ *sound-srate* 2.0)) - (format nil - (_ "Error:~%~%Frequency (~a Hz) is too high for track sample rate.~%~%~ - Track sample rate is ~a Hz~%~ - Frequency must be less than ~a Hz.") - FREQUENCY - *sound-srate* - (/ *sound-srate* 2.0))) - (T (funcall (nth ROLLOFF '(hp highpass2 highpass4 highpass6 highpass8)) - *track* FREQUENCY))) diff --git a/Release/plug-ins/label-sounds.ny b/Release/plug-ins/label-sounds.ny deleted file mode 100644 index d21d583b76f5166f13abcd6d0f105f1eb238954b..0000000000000000000000000000000000000000 --- a/Release/plug-ins/label-sounds.ny +++ /dev/null @@ -1,259 +0,0 @@ -$nyquist plug-in -$version 4 -$type analyze -;i18n-hint: Name of effect that labels sounds -$name (_ "Label Sounds") -$debugbutton false -$author (_ "Steve Daulton") -$release 3.0.4-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control THRESHOLD (_ "Threshold level (dB)") float "" -30 -100 0 -$control MEASUREMENT (_ "Threshold measurement") choice (("peak" (_ "Peak level")) - ("avg" (_ "Average level")) - ("rms" (_ "RMS level"))) 0 -$control SIL-DUR (_ "Minimum silence duration") time "" 1 0.01 3600 -$control SND-DUR (_ "Minimum label interval") time "" 1 0.01 7200 -$control TYPE (_ "Label type") choice (("before" (_ "Point before sound")) - ("after" (_ "Point after sound")) - ("around" (_ "Region around sounds")) - ("between" (_ "Region between sounds"))) 2 -$control PRE-OFFSET (_ "Maximum leading silence") time "" 0 0 nil -$control POST-OFFSET (_ "Maximum trailing silence") time "" 0 0 nil -;i18n-hint: Do not translate '##1' -$control TEXT (_ "Label text") string "" (_ "Sound ##1") - - -(setf thresh-lin (db-to-linear THRESHOLD)) -(setf max-labels 10000) ;max number of labels to return - - -(defun format-time (s) - ;;; format time in seconds as h m s. - ;;; (Only used for error message if selection > 2^31 samples.) - (let* ((hh (truncate (/ s 3600))) - (mm (truncate (/ s 60)))) - ;i18n-hint: hours minutes and seconds. Do not translate "~a". - (format nil (_ "~ah ~am ~as") - hh (- mm (* hh 60)) (rem (truncate s) 60)))) - - -(defun parse-label-text () - ;;; Special character '#' represents an incremental digit. - ;;; Return '(digits num pre-txt post-txt) for - ;;; (number-of-digits, initial-value, text-before-number, text-after-number), - ;;; or NIL. - ;;; 'initial-value' is a positive integer or zero (default). - ;;; Only the first instance of #'s are considered 'special'. - (let ((hashes 0) - (num nil) - (negative nil) - (pre-txt "") - (post-txt "") - ch) - (dotimes (i (length TEXT)) - (setf ch (char TEXT i)) - (cond - ((and (string= post-txt "") (char= ch #\#)) - (incf hashes)) - ((and (> hashes 0) (string= post-txt "")) - (cond - ((digit-char-p ch) - (if num - (setf num (+ (* num 10) (digit-char-p ch))) - (setf num (digit-char-p ch)))) - ((and (not num)(char= ch #\-)) - (setf negative t)) - (t (setf post-txt (string ch))))) - ((= hashes 0) ;special '#' not yet found - (string-append pre-txt (string ch))) - (t ;run out of #'s and digit characters. - (string-append post-txt (string ch))))) - (when negative - (setf num (- num))) - ;; Replace string literal hash characters. - (when (and (> hashes 0) (not num)) - (dotimes (i hashes) - (string-append pre-txt "#"))) - (list hashes num pre-txt post-txt))) - - -(defun pad (n d) - ;;; Return string, int 'n' padded to 'd' digits, or empty string. - ;;; Used in formatting label text. - (cond - (n - (let ((negative (minusp n)) - (n (format nil "~a" (abs n)))) - (while (< (length n) d) - (setf n (format nil "0~a" n))) - (if negative - (format nil "-~a" n) - n))) - (t ""))) - - -(defun to-mono (sig) - ;;; Coerce sig to mono. - (if (arrayp sig) - (s-max (s-abs (aref sig 0)) - (s-abs (aref sig 1))) - sig)) - - -(defun to-avg-mono (sig) - ;;; Average of stereo channels - (if (arrayp sig) - (mult 0.5 (sum (aref sig 0)(aref sig 1))) - sig)) - - -(defun reduce-srate (sig) - ;;; Reduce sample rate to (about) 100 Hz. - (let ((ratio (round (/ *sound-srate* 100)))) - (cond - ((= MEASUREMENT 0) ;Peak - (let ((sig (to-mono sig))) - (snd-avg sig ratio ratio OP-PEAK))) - ((= MEASUREMENT 1) ;Average absolute level - (let ((sig (to-avg-mono (s-abs sig)))) - (snd-avg sig ratio ratio OP-AVERAGE))) - (t ;RMS - (if (arrayp sig) - ;; Stereo RMS is the root mean of all (samples ^ 2) [both channels] - (let* ((sig (mult sig sig)) - (left-mean-sq (snd-avg (aref sig 0) ratio ratio OP-AVERAGE)) - (right-mean-sq (snd-avg (aref sig 1) ratio ratio OP-AVERAGE))) - (s-sqrt (mult 0.5 (sum left-mean-sq right-mean-sq)))) - (rms sig)))))) - - -(defun find-sounds (sig srate) - ;;; Return a list of sounds that are at least 'SND-DUR' long, - ;;; separated by silences of at least 'SIL-DUR'. - (let ((sel-start (get '*selection* 'start)) - (snd-list ()) - (sample-count 0) - (sil-count 0) - (snd-count 0) - (snd-start 0) - (label-count 0) - (snd-samples (* SND-DUR srate)) - (sil-samples (* SIL-DUR srate))) - ;;Ignore samples before time = 0 - (when (< sel-start 0) - (setf sample-count (truncate (* (abs sel-start) srate))) - (dotimes (i sample-count) - (snd-fetch sig))) - ;;Main loop to find sounds. - (do ((val (snd-fetch sig) (snd-fetch sig))) - ((not val) snd-list) - (cond - ((< val thresh-lin) - (when (and (>= sil-count sil-samples)(>= snd-count snd-samples)) - ;convert sample counts to seconds and push to list. - (push (list (/ snd-start srate) - (/ (- sample-count sil-count) srate)) - snd-list) - (incf label-count) - (when (= label-count max-labels) - (format t (_ "Too many silences detected.~%Only the first 10000 labels added.")) - (return-from find-sounds snd-list)) - (setf snd-count 0)) ;Pushed to list, so reset sound sample counter. - (when (> snd-count 0) ;Sound is shorter than snd-samples, so keep counting. - (incf snd-count)) - (incf sil-count)) - ;; Above threshold. - (t (when (= snd-count 0) ;previous sound was push, so this is a new sound. - (setf snd-start sample-count)) - (setf sil-count 0) - (incf snd-count))) - (incf sample-count)) - ;; Check for final sound - (when (> snd-count 0) - (push (list (/ snd-start srate) - (/ (- sample-count sil-count) srate)) - snd-list)) - snd-list)) - - -(defun return-labels (snd-list) - (setf textstr (parse-label-text)) - ; Selection may extend before t=0 - ; Find t=0 relative to selection so we can ensure - ; that we don't create hidden labels. - (setf t0 (- (get '*selection* 'start))) - (setf t1 (- (get '*selection* 'end))) - (let ((label-start t0) - (label-end t1) - (label-text "") - (labels ()) - (final-sound (if (= TYPE 3) 1 0)) ;TYPE 3 = regions between sounds. - ;; Assign variables to parsed label text - (digits (first textstr)) - (num (second textstr)) - (pre-txt (third textstr)) - (post-txt (fourth textstr))) - ;snd-list is in reverse chronological order - (do ((i (1- (length snd-list)) (1- i))) - ((< i final-sound) labels) - (case TYPE - (3 ;;label silences. - (setf start-time (second (nth i snd-list))) - (setf end-time (first (nth (1- i) snd-list))) - ;don't overlap next sound - (setf label-start (min end-time (+ start-time PRE-OFFSET))) - ;don't overlap previous sound - (setf label-end (max start-time (- end-time POST-OFFSET))) - ;ensure end is not before start - (when (< (- label-end label-start) 0) - (setf label-start (/ (+ label-end label-start) 2.0)) - (setf label-end label-start))) - (t ;; labelling sounds - (setf start-time (first (nth i snd-list))) - (setf end-time (second (nth i snd-list))) - ;don't overlap t0 or previous sound. - (setf label-start (max t0 label-start (- start-time PRE-OFFSET))) - (setf label-end (+ end-time POST-OFFSET)) - ;; Don't overlap following sounds. - (when (> i 0) - (setf label-end (min label-end (first (nth (1- i) snd-list))))))) - (setf label-text (format nil - "~a~a~a" - pre-txt - (pad num digits) - post-txt)) - (case TYPE - (0 (push (list label-start label-text) labels)) ;point label before sound - (1 (push (list label-end label-text) labels)) ;point label after sound - (2 (push (list label-start label-end label-text) labels)) ;sound region - (t (push (list label-start label-end label-text) labels)));silent region - ;Earliest allowed start time for next label. - (setf label-start end-time) - ;num is either an int or nil - (when num (incf num))))) - - -(let ((sig (reduce-srate *track*))) - (setf *track* nil) - (setf snd-list (find-sounds sig (snd-srate sig))) - (cond - ((= (length snd-list) 0) - (format nil - (_ "No sounds found.~%~ - Try lowering 'Threshold level (dB)'."))) - ((and (= TYPE 3) - (= (length snd-list) 1)) - (format nil - (_ "Labelling regions between sounds requires~%~ - at least two sounds.~%~ - Only one sound detected."))) - (t - (return-labels snd-list)))) diff --git a/Release/plug-ins/limiter.ny b/Release/plug-ins/limiter.ny deleted file mode 100644 index 327331747aae18fe8e3f80e9fcb651a07eb87f81..0000000000000000000000000000000000000000 --- a/Release/plug-ins/limiter.ny +++ /dev/null @@ -1,135 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$name (_ "Limiter") -$debugbutton false -$preview enabled -$author (_ "Steve Daulton") -$release 3.0.4-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; limiter.ny by Steve Daulton November 2011, updated May 2015. - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control TYPE (_ "Type") choice (("SoftLimit" (_ "Soft Limit")) - ("HardLimit" (_ "Hard Limit")) -;i18n-hint: clipping of wave peaks and troughs, not division of a track into clips - ("SoftClip" (_ "Soft Clip")) - ("HardClip" (_ "Hard Clip"))) 0 - -;; Translations don't support "\n", and widgets need a literal string, -;; so the next two controls must be written on two lines. -$control GAIN-L (_ "Input Gain (dB) -mono/Left") real "" 0 0 10 - -$control GAIN-R (_ "Input Gain (dB) -Right channel") real "" 0 0 10 - -$control THRESH (_ "Limit to (dB)") real "" -3 -10 0 -$control HOLD (_ "Hold (ms)") real "" 10 1 50 -$control MAKEUP (_ "Apply Make-up Gain") choice ((_ "No") (_ "Yes")) 0 - - -(setf gain-left (db-to-linear GAIN-L)) -(setf gain-right (db-to-linear GAIN-R)) -(setf thresh-lin (db-to-linear THRESH)) -(setf bmakeup (= MAKEUP 1)) - - -;;; brick wall limiter -(defun hardlimit (sig limit) - (let* ((time (/ HOLD 3000.0)) ; lookahead time (seconds) - (samples (round (* time *sound-srate*))) ; lookahead in samples - (peak-env (get-env sig samples time limit))) - (mult sig - (snd-exp (mult -1 (snd-log peak-env)))))) - - -;;; Envelope follower for brick wall limiter -(defun get-env (sig step lookahead limit) - (let* ((sig (mult (/ limit) sig)) - (pad-time (* 3 lookahead)) ; padding required at start (seconds) - (pad-s (* 3 step)) ; padding samples - (padding (snd-const (peak sig pad-s) 0 *sound-srate* pad-time)) - (peak-env (snd-avg sig (* 4 step) step OP-PEAK))) - (extract 0 1 - (s-max 1 - (sim padding - (at-abs pad-time (cue peak-env))))))) - - -(defun softlimit (sig threshold) - (let* ((sig (hardlimit sig 1)) - (step (truncate (* (/ HOLD 3000.0) *sound-srate*))) - (waveshape (snd-avg sig (* 4 step) step op-peak)) - (env (sum threshold (mult threshold (diff 1 waveshape)))) - (env (clip env 1)) - (offset (/ (* 3 step) *sound-srate*)) - (initial (peak sig (* 2 step))) - (pause-lev (sum threshold (mult threshold (diff 1 initial)))) - (pause-lev (clip pause-lev 0.9)) - (pause (snd-const pause-lev 0 *sound-srate* offset))) - (setf env (sim pause - (at-abs offset (cue env)))) - (mult sig env))) - - -(defun soft-clip-table () - ;;; Lookup table for soft clipping wave-shaper. - (abs-env - (sound-srate-abs 44100 - (control-srate-abs 44100 - (let* ((knee (- 1 (/ 1.0 pi))) - (kcurve (mult knee (osc (hz-to-step (/ (* 4 knee))) knee))) - (ikcurve (mult knee (osc (hz-to-step (/ (* 4 knee))) knee *sine-table* -90))) - (lin (pwlv -0.5 knee -0.5 - (+ knee (/ 2.0 pi)) 0.5 - 2.0 0.5 - 2.0 (+ 0.5 knee) - 2.1 (+ 0.5 knee)))) - (mult (/ 2.0 pi) - (sim (at-abs 0 (cue ikcurve)) - (at-abs 0 (cue lin)) - (at-abs (+ knee (/ 2.0 pi)) (cue kcurve))))))))) - - -(defun soft-clip (sig threshold) - (let* ((knee (- 1 (/ 1.0 pi))) - (clip-level (* (+ 0.5 knee)(/ 2.0 pi))) - (sig (mult clip-level (/ threshold) sig))) - (if bmakeup - ; Allow a little overhead to avoid hitting 0dB. - (mult (/ 0.999 clip-level) - (shape sig (soft-clip-table) 1.0)) - (mult (/ threshold clip-level) - (shape sig (soft-clip-table) 1.0))))) - - -(defun makeupgain (sig threshold) - (if bmakeup - (mult (/ 0.999 threshold) sig) ;keep below 0dB - sig)) - - -;; Pre-gain -(setf *track* - (if (arrayp *track*) - (vector (mult (aref *track* 0) gain-left) - (mult (aref *track* 1) gain-right)) - (mult *track* gain-left))) - - -(case TYPE - (0 (makeupgain (multichan-expand #'softlimit *track* thresh-lin) - thresh-lin)) - (1 (makeupgain (multichan-expand #'hardlimit *track* thresh-lin) - thresh-lin)) - (2 (soft-clip *track* thresh-lin)) - (T (makeupgain (clip *track* thresh-lin) - thresh-lin))) diff --git a/Release/plug-ins/lowpass.ny b/Release/plug-ins/lowpass.ny deleted file mode 100644 index a2a44194176f75e8b9c9a1006c32664829600a90..0000000000000000000000000000000000000000 --- a/Release/plug-ins/lowpass.ny +++ /dev/null @@ -1,40 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview linear -$name (_ "Low-Pass Filter") -$debugbutton disabled -$author (_ "Dominic Mazzoni") -$release 2.3.0-2 -$copyright (_ "GNU General Public License v2.0") - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control FREQUENCY (_ "Frequency (Hz)") float-text "" 1000 0 nil -$control ROLLOFF (_ "Roll-off (dB per octave)") choice (("dB6" (_ "6 dB")) - ("dB12" (_ "12 dB")) - ("dB24" (_ "24 dB")) - ("dB36" (_ "36 dB")) - ("dB48" (_ "48 dB"))) 0 - - -(cond - ; Validate minimum frequency at run time so we can give a - ; less cryptic error message than built-in widget validation. - ((< FREQUENCY 0.1) - (_ "Frequency must be at least 0.1 Hz.")) - ((>= FREQUENCY (/ *sound-srate* 2.0)) - (format nil - (_ "Error:~%~%Frequency (~a Hz) is too high for track sample rate.~%~%~ - Track sample rate is ~a Hz~%~ - Frequency must be less than ~a Hz.") - FREQUENCY - *sound-srate* - (/ *sound-srate* 2.0))) - (T (funcall (nth ROLLOFF '(lp lowpass2 lowpass4 lowpass6 lowpass8)) - *track* FREQUENCY))) diff --git a/Release/plug-ins/noisegate.ny b/Release/plug-ins/noisegate.ny deleted file mode 100644 index 480539cb54c8091ff1bc736f324a22b911188f60..0000000000000000000000000000000000000000 --- a/Release/plug-ins/noisegate.ny +++ /dev/null @@ -1,174 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$name (_ "Noise Gate") -$debugbutton false -$preview enabled -$author (_ "Steve Daulton") -$release 3.0.4-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html . -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control MODE (_ "Select Function") choice (("Gate" (_ "Gate")) - ("Analyze" (_ "Analyze Noise Level"))) 0 -$control STEREO-LINK (_ "Stereo Linking") choice (("LinkStereo" (_ "Link Stereo Tracks")) - ("DoNotLink" (_ "Don't Link Stereo"))) 0 -;; Work around bug 2336 - Text after control is not read by screen reader. -$control THRESHOLD (_ "Gate threshold (dB)") float "" -40 -96 -6 -$control GATE-FREQ (_ "Gate frequencies above (kHz)") float "" 0 0 10 -$control LEVEL-REDUCTION (_ "Level reduction (dB)") float "" -24 -100 0 -$control ATTACK (_ "Attack (ms)") float "" 10 1 1000 -$control HOLD (_ "Hold (ms)") float "" 50 0 2000 -$control DECAY (_ "Decay (ms)") float "" 100 10 4000 - - -;; The gain envelope for the noisegate function may be a mono sound (STEREO-LINK = 1, or *track* is mono) -;; or an array of sounds (STEREO-LINK = 0 and *track* is stereo). -;; 'Level Reduction' is similar to "Range" or "Floor", but is a (negative) amount of gain -;; rather than a fixed level. -;; -;; To create the gain envelope: -;; 1. If stereo track and STEREO-LINK = 1, get the max of left and right. -;; 2. Add 'hold' signal when level > THRESHOLD. -;; This adds a high level signal for 'HOLD' seconds when the level -;; falls below the THRESHOLD. -;; 3. Nyquist GATE function to generate exponential rise and decay. -;; Unlike analog noise gates, lookahead is used so that the gate -;; begins to open before the signal rises above the THRESHOLD. -;; When the THRESHOLD is reached, the gate is fully open. -;; This prevents the gate from clipping the beginning of words / sounds. -;; 4. Scale level of envelope and offset so that we have unity gain above -;; THRESHOLD, and 'LEVEL-REDUCTION' below the THRESHOLD. -;; If SILENCE-FLAG is set (= 1), gain below the THRESHOLD is zero. - - -; Global variables (treat as constants). -(setf SILENCE-FLAG (if (> LEVEL-REDUCTION -96) 0 1)) -(setf GATE-FREQ (* 1000.0 GATE-FREQ)) -(setf FLOOR (db-to-linear LEVEL-REDUCTION)) -(setf THRESHOLD (db-to-linear THRESHOLD)) -(setf ATTACK (/ ATTACK 1000.0)) -(setf LOOKAHEAD ATTACK) -(setf DECAY (/ DECAY 1000.0)) -(setf HOLD (/ HOLD 1000.0)) - - -(defun error-check () - (let ((max-hz (* *sound-srate* 0.45)) ;10% below Nyquist should be safe maximum. - (max-khz (roundn (* 0.00045 *sound-srate*) 1)) - (gate-freq-khz (roundn (/ GATE-FREQ 1000.0) 1))) - (when (>= GATE-FREQ max-hz) - (throw 'err (format nil - (_ "Error.~%~ - Gate frequencies above: ~s kHz~%~ - is too high for selected track.~%~ - Set the control below ~a kHz.") - gate-freq-khz - max-khz)))) - (when (< len 100) ;100 samples required - (throw 'err (format nil - (_ "Error.~%~ - Insufficient audio selected.~%~ - Make the selection longer than ~a ms.") - (round-up (/ 100000 *sound-srate*)))))) - - -;;; Analysis functions: -;; Measure the peak level (dB) and suggest setting threshold a little higher. - -(defun analyze (sig) - ; Return analysis text. - (let* ((test-length (truncate (min len (/ *sound-srate* 2.0)))) - (peakdb (peak-db sig test-length)) - (target (+ 1.0 peakdb))) ;suggest 1 dB above noise level - (format nil - (_ "Peak based on first ~a seconds ~a dB~%~ - Suggested Threshold Setting ~a dB.") - (roundn (/ test-length *sound-srate*) 2) - (roundn peakdb 2) - (roundn target 0)))) - - -(defun peak-db (sig test-len) - ;; Return absolute peak (dB). - ;; For stereo tracks, return the maximum of the channels. - (if (arrayp sig) - (let ((peakL (peak (aref sig 0) test-len)) - (peakR (peak (aref sig 1) test-len))) - (linear-to-db (max peakL peakR))) - (linear-to-db (peak sig test-len)))) - - -;;; Utility functions - -(defun round-up (num) - (round (+ num 0.5))) - - -(defun roundn (num places) - ;; Return number rounded to specified decimal places. - (if (= places 0) - (round num) - (let* ((x (format NIL "~a" places)) - (ff (strcat "%#1." x "f"))) - (setq *float-format* ff) - (format NIL "~a" num)))) - - -(defun format-time (s) - ;;; format time in seconds as h m. - (let* ((hh (truncate (/ s 3600))) - (mm (truncate (/ s 60)))) - ;i18n-hint: hours and minutes. Do not translate "~a". - (format nil (_ "~ah ~am") hh (- mm (* hh 60))))) - - -;;; Gate Functions - -(defun noisegate (sig follow) - ;; Takes a sound and a 'follow' sound as arguments. - ;; Returns the gated audio. - (let ((gain (/ (- 1 (* SILENCE-FLAG FLOOR)))) ; SILENCE-FLAG is 0 or 1. - (env (get-env follow))) - (if (> GATE-FREQ 20) - (let* ((high (highpass8 sig GATE-FREQ)) - (low (lowpass8 sig (* 0.91 GATE-FREQ)))) ;magic number 0.91 improves crossover. - (sim (mult high gain env) low)) - (mult sig gain env)))) - - -(defun get-env (follow) - ;; Return gate's envelope - (let* ((gate-env (gate follow LOOKAHEAD ATTACK DECAY FLOOR THRESHOLD)) - (gate-env (clip gate-env 1.0))) ;gain must not exceed unity. - (diff gate-env (* SILENCE-FLAG FLOOR)))) - - -(defun peak-follower (sig) - ;; Return signal that gate will follow. - (setf sig (multichan-expand #'snd-abs sig)) - (when (and (arrayp sig)(= STEREO-LINK 0)) - (setf sig (s-max (aref sig 0) (aref sig 1)))) - (if (> HOLD 0) - (multichan-expand #'snd-oneshot sig THRESHOLD HOLD) - sig)) - - -(defun process () - (error-check) - ;; For stereo tracks, 'peak-follower' may return a sound - ;; or array of sounds, so pass it to 'noisegate' rather than - ;; calculating in 'noisegate'. - (multichan-expand #' noisegate *track* (peak-follower *track*))) - - -;; Run program -(case MODE - (0 (catch 'err (process))) - (T (analyze *track*))) diff --git a/Release/plug-ins/notch.ny b/Release/plug-ins/notch.ny deleted file mode 100644 index e81fd7c4c062220df18f7772af1068c771916007..0000000000000000000000000000000000000000 --- a/Release/plug-ins/notch.ny +++ /dev/null @@ -1,32 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview linear -$name (_ "Notch Filter") -$debugbutton false -$author (_ "Steve Daulton and Bill Wharrie") -$release 2.3.0-2 -$copyright (_ "GNU General Public License v2.0 or later") - - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control FREQUENCY (_ "Frequency (Hz)") float-text "" 60 0 nil -$control Q (_ "Q (higher value reduces width)") float-text "" 1 0.1 1000 - -(cond - ((< FREQUENCY 0.1) (_ "Frequency must be at least 0.1 Hz.")) - ((>= FREQUENCY (/ *sound-srate* 2.0)) - (format nil - (_ "Error:~%~%Frequency (~a Hz) is too high for track sample rate.~%~%~ - Track sample rate is ~a Hz.~%~ - Frequency must be less than ~a Hz.") - FREQUENCY - *sound-srate* - (/ *sound-srate* 2.0))) - (T (notch2 *track* FREQUENCY Q))) diff --git a/Release/plug-ins/nyquist-plug-in-installer.ny b/Release/plug-ins/nyquist-plug-in-installer.ny deleted file mode 100644 index 8a5502b90ff1ba4d9bdf4c50b33f338fa8e5b5b5..0000000000000000000000000000000000000000 --- a/Release/plug-ins/nyquist-plug-in-installer.ny +++ /dev/null @@ -1,257 +0,0 @@ -$nyquist plug-in -$version 4 -$type tool -$name (_ "Nyquist Plugin Installer") -$debugbutton false -$preview disabled -$author "Steve Daulton" -$release 2.4.0-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -;i18n-hint: "Browse..." is text on a button that launches a file browser. -$control FILES (_ "Select file(s) to install") file (_ "Browse...") "~/Desktop/" (((_ "Plug-in") (ny NY)) - ((_ "Lisp file") (lsp LSP)) - ((_ "HTML file") (htm HTM html HTML)) - ((_ "Text file") (txt TXT)) - ((_ "All supported") (ny NY lsp LSP htm HTM html HTML txt TXT)) - ((_ "All files") (""))) "open,exists,multiple" -$control OVERWRITE (_ "Allow overwriting") choice ((_ "Disallow") (_ "Allow")) 0 - - -(defun audacity-version-ok (min-version) - ;; No longer required as this plug-in is shipped with Audacity. - ;; Left in for illustration purposes. - ;; min-version is a list of three numbers (the minimum Audacity version number). - ;; Example, if the minimum version required is Audacity 2.4.0, then - ;; call (audacity-version-ok '(2 4 0)) - ;; Returns t if plug-in is running on 2.4.0 or later, otherwise nil. - (cond - ((get '*audacity* 'version) - (mapc (lambda (x y) - (cond - ((boundp 'isok)) - ((> x y) (setf isok t)) - ((< x y) (setf isok nil)))) - (get '*audacity* 'version) - min-version) - (or (not (boundp 'isok)) isok)) - (t nil))) - - -(defun get-file-name (fqname &aux (fname "")) - ;; Return file name . extension from fully qualified file name. - (dotimes (i (length fqname) fname) - (if (char= (char fqname i) *file-separator*) - (setf fname "") - (setf fname (format nil "~a~a" fname (char fqname i)))))) - - -(defun isfilename (fname) - ;; Return t if fname looks like valid file name, else nil. - (let ((ln (length fname))) - (cond - ((= ln 0) nil) - ((char= (char fname (- ln 1)) *file-separator*) nil) - (t t)))) - - -(defun existsp (fname) - ;; Return t if file exists, else nil. - (let ((fp (open fname))) - (cond - (fp (close fp) - t) - (t nil)))) - - -(defun writeablep (fname) - ;; Return t if file is writeable. - (let ((fp (open fname :direction :output))) - (cond - (fp (close fp) t) - (t nil)))) - - -(defun copy-file (input output) - ;; Copy from input file to output file. - (let ((ifp (open input :direction :input)) - (ofp (open output :direction :output))) - (do ((line (read-line ifp)(read-line ifp))) - ((not line)) - (format ofp "~a~%" line)) - (close ifp) - (close ofp))) - - -(defun issupported (fname) - ;; Return true if it looks like a supported file. - ;; For .lsp and .html files, we only check the file extension. - ;; For .ny files, we have additional sanity checks that it is a - ;; plug-in and not just a Nyquist Prompt script. - (let ((goodfname (fix-ext fname))) - (cond - ((check-ext goodfname ".lsp") t) - ((check-ext goodfname ".htm") t) - ((check-ext goodfname ".html") t) - ((check-ext goodfname ".txt") t) - ((not (check-ext goodfname ".ny")) nil) - ((has-plugin-header fname) t) - (t nil)))) - - -(defun check-ext (fname ext) - ;; Return true if fname has extension ext. - (let* ((fnameln (length fname)) - (extln (length ext)) - (restln (- fnameln extln))) - (cond - ((< fnameln (1+ extln)) nil) ; Too short to be valid. - ((string-equal (subseq fname restln fnameln) ext) t) - (t nil)))) - - -(defun fix-ext (fname) - ;; If string ends in ".ny.txt" or ".lsp.txt", strip off ".txt" - (macrolet ((striptxt (fname) `(setf ,fname (subseq ,fname 0 (- ln 4))))) - (let ((ln (length fname))) - (cond - ((and (> ln 8) (string-equal (subseq fname (- ln 8) ln) ".lsp.txt")) - (striptxt fname)) - ((and (> ln 7) (string-equal (subseq fname (- ln 7) ln) ".ny.txt")) - (striptxt fname))) - fname))) - - -(defun has-plugin-header (fname) - ;; Return t if file looks like valid Nyquist plug-in, else nil. - (let ((fp (open fname)) - (teststring "nyquist plug-in")) - ; First char may be #\; or #\$ - (setf b (read-byte fp)) - (cond - ((and (/= b (char-code #\;))(/= b (char-code #\$))) - (close fp) - nil) - ((do* ((i 0 (1+ i)) - (b (read-byte fp) (read-byte fp)) - (test (char-code (char teststring i)) - (char-code (char teststring i)))) - ((= i (1- (length teststring))) t) - (when (/= b test) - (return))) - (close fp) - t) - (t - (close fp) - nil)))) - - -(defun get-file-list (file-string) - ;; See https://wiki.audacityteam.org/wiki/Nyquist_File-Button_Tutorial#Open_Multiple_Files - (let ((path-string (format nil "(list ~s )" (string-trim "\"" file-string)))) - (eval-string path-string))) - - -(defun install (fname) - ;; Install file fname (fully qualified file name). - ;; Push result to list install-success or install-fail. - (setf out-path (get '*system-dir* 'user-plug-in)) - (setf short-name (get-file-name fname)) - (cond - ((not (existsp fname)) - (push (list 3 fname) install-fail)) - ((not (issupported fname)) - (push (list 4 fname) install-fail)) - (t - (setf short-name (fix-ext short-name)) - (setf out-fname - (format nil "~a~a~a" out-path *file-separator* short-name)) - (setf out-file-exists (existsp out-fname)) - (cond - ;; Check for fails - ((and out-file-exists - (= OVERWRITE 0)) - (push (list 5 short-name) install-fail)) - ((not (writeablep out-fname)) - (push (list 6 short-name) install-fail)) - ;; Now the successes - ((check-ext short-name ".ny") - (copy-file fname out-fname) - (if (and out-file-exists - (= OVERWRITE 1)) - (push (list 1 short-name) install-success) - (push (list 0 short-name) install-success))) - ;; Output file is writeable and did not previously exist. - (t (copy-file fname out-fname) - (push (list 2 short-name) install-success)))))) - - -(defun print-results (&aux msg results) - ;; Format results and display in human readable form. - (cond - ((isempty install-success) - (setf msg (format nil (_ "Error.~%")))) - ((isempty install-fail) - (setf msg (format nil (_ "Success.~%Files written to:~%~s~%") - (get '*system-dir* 'user-plug-in)))) - (t (setf msg (format nil (_ "Warning.~%Failed to copy some files:~%"))))) - (setf results (append install-success install-fail)) - (setf results (sort-results results)) - (let ((status -1)) - (dolist (m results msg) - (when (/= (first m) status) - (setf msg (format nil "~a~%~a~%" msg (status (first m)))) - (setf status (first m))) - (setf msg (format nil "~a~a~%" msg (second m)))))) - - -(defun isempty (x) - ;;Return t if x is an empty list. - (unless (listp x) - (error "Not a list" x)) - (if (= (length x) 0) t nil)) - - -(defun isnotempty (x) - (not (isempty x))) - - -(defun status (num) - ;; Return status message corresponding to the installation status number. - ;; This allows result messages to be grouped according to installation status. - (case num ; Success - ; Translations fail when strings contain control characters, so - ; use FORMAT directive "~%" instead of "\n" for new line. - (0 (format nil (_ "Plug-ins installed.~%(Use the Plug-in Manager to enable effects):"))) - (1 (_ "Plug-ins updated:")) - (2 (_ "Files copied to plug-ins folder:")) - ;; Fail - (3 (_ "Not found or cannot be read:")) - (4 (_ "Unsupported file type:")) - (5 (_ "Files already installed ('Allow Overwriting' disabled):")) - (6 (_ "Cannot be written to plug-ins folder:")))) - - -(defun sort-results (results) - ;; 'results' are either 'install-success' or 'install-fail'. - ;; Each item in results is (list status file-name). - ;; Returns 'results' sorted by status number. - (sort results #'(lambda (x y) (< (car x) (car y))))) - - -;; Global lists -(setf install-success ()) -(setf install-fail ()) - -(let ((file-list (get-file-list FILES))) - (if (= (length file-list) 0) - (format nil (_ "Error.~%No file selected.")) - (dolist (file file-list (print-results)) - (install file)))) diff --git a/Release/plug-ins/pluck.ny b/Release/plug-ins/pluck.ny deleted file mode 100644 index d95313c9920d1f41cd0c0f2f3f6aee0dc112feba..0000000000000000000000000000000000000000 --- a/Release/plug-ins/pluck.ny +++ /dev/null @@ -1,48 +0,0 @@ -$nyquist plug-in -$version 4 -$type generate -$name (_ "Pluck") -$debugbutton false -$preview linear -$author (_ "David R.Sky") -$release 2.4.2 -$copyright (_ "GNU General Public License v2.0") - - -;;MIDI values for C notes: 36, 48, 60 [middle C], 72, 84, 96. - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control PITCH (_ "Pluck MIDI pitch") int "" 60 1 120 -$control FADE (_ "Fade-out type") choice ((_ "Abrupt") (_ "Gradual")) 0 -$control DUR (_ "Duration (60s max)") time "" 1 0 60 - - -; set final-amp for abrupt or gradual fade -(setf final-amp (if (= FADE 1) 0.001 0.000001)) - -(cond - ((> DUR 0) - ;; Get length of preview - (setq pdur - (if *previewp* - (get '*project* 'preview-duration) - DUR)) - - (let* ((pluck-sound (snd-pluck *sound-srate* (step-to-hz PITCH) 0 DUR final-amp)) - (pluck-sound (extract-abs 0 pdur pluck-sound)) ; shorten if necessary for preview. - (max-peak (peak pluck-sound ny:all))) - ;; snd-pluck has a random element and will occasionally produce - ;; zero amplitude at very high pitch settings. Avoid division by zero. - (if (> max-peak 0) - (scale (/ 0.8 max-peak) pluck-sound) - pluck-sound))) - ;; Length of sound is zero! - ;; If previewing give Audacity a bit of silence, else return null string. - (*previewp* (s-rest 0.1)) - (t "")) diff --git a/Release/plug-ins/rhythmtrack.ny b/Release/plug-ins/rhythmtrack.ny deleted file mode 100644 index 981188f7bd7a8cc3dfd6229f3c395173aeac0acb..0000000000000000000000000000000000000000 --- a/Release/plug-ins/rhythmtrack.ny +++ /dev/null @@ -1,247 +0,0 @@ -$nyquist plug-in -$version 4 -$type generate -$name (_ "Rhythm Track") -$debugbutton false -$preview linear -$author (_ "Dominic Mazzoni, David R. Sky and Steve Daulton") -$release 3.0.0-2 -$copyright (_ "GNU General Public License v2.0") - - -;; Drip sound generator by Paul Beach - -;; TODO: add more drum sounds - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control TEMPO (_ "Tempo (bpm)") real (_ "30 - 300 beats/minute") 120 30 300 -$control TIMESIG (_ "Beats per bar") int (_ "1 - 20 beats/measure") 4 1 20 -$control SWING (_ "Swing amount") float (_ "+/- 1") 0 -1 1 -$control text (_ "Set 'Number of bars' to zero to enable the 'Rhythm track duration'.") -$control BARS (_ "Number of bars") int (_ "1 - 1000 bars") 16 0 1000 -$control CLICK-TRACK-DUR (_ "Rhythm track duration") time (_ "Used if 'Number of bars' = 0") 0 0 nil -$control OFFSET (_ "Start time offset") time (_ "Silence before first beat") 0 0 nil -$control CLICK-TYPE (_ "Beat sound") choice (("Metronome" (_ "Metronome Tick")) - (_ "Ping (short)") - (_ "Ping (long)") - (_ "Cowbell") - ("ResonantNoise" (_ "Resonant Noise")) - ("NoiseClick" (_ "Noise Click")) - (_ "Drip (short)") - (_ "Drip (long)")) 0 - -$control HIGH (_ "MIDI pitch of strong beat") int (_ "18 - 116") 84 18 116 -$control LOW (_ "MIDI pitch of weak beat") int (_ "18 - 116") 80 18 116 - - -;; Helper functions: - -(defun round-up (x) - (if (> x (truncate x)) - (truncate (1+ x)) - (truncate x))) - - -;; Filtering causes changes amplitude, so we normalize to -;; achieve a predictable level. -(defun normalize (sig) - (scale (/ (peak sig ny:all)) sig)) - - -(defun s-rest-abs (d) - (abs-env (s-rest d))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Click sound synthesis -;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;; Drip sound by Paul Beach www.proviewlandscape.com/liss/ -(defun drip (p) ;p is pitch in hz - (let* ((maxhz (/ *sound-srate* 2.1)) - (hz1 (min maxhz (* 2.40483 p))) - (hz2 (min maxhz (* 5.52008 p))) - (hz3 (min maxhz (* 8.653 p))) - (hz4 (min maxhz (* 11.8 p)))) - (lp - (stretch-abs 1 - (mult (exp-dec 0 0.015 0.25) - (sim - (mult (hzosc hz1) 0.5) - (mult (hzosc hz2) 0.25) - (mult (hzosc hz3) 0.125) - (mult (hzosc hz4) 0.0625)))) - 440))) - - -;; Metronome tick by Steve Daulton. -(defun metronome-tick (hz peak) - (let* ((ln 300) - (sig-array (make-array ln)) - (x 1)) - ;; generate some 'predictable' white noise - (dotimes (i ln) - (setf x (rem (* 479 x) 997)) - (setf (aref sig-array i) (- (/ x 500.0) 1))) - (setf sig (sim (s-rest-abs 0.2) - (snd-from-array 0 44100 sig-array))) - (setf sig - (mult (abs-env (pwev 10 (/ ln 44100.0) 2 1 0)) - (highpass8 (lowpass2 sig (* 2 hz) 6) - hz))) - (let ((gain (/ (peak sig 300)))) - ; The '1.11' factor makes up for gain reduction in 'resample' - (mult (abs-env (pwlv 1.11 0.02 1.11 0.05 0 )) - (jcrev (mult peak gain sig) 0.01 0.1))))) - - -;; Cowbell by Steve Daulton. -(defun cowbell (hz) - (sim - (mult (pwev 0.3 0.8 0.0005) - (hzosc hz *tri-table*) - (hzosc (* hz 3.46) *tri-table*)) - (mult (pwev 0.7 0.2 0.01) - (hzosc (* hz 7.3) *tri-table*) - (hzosc (* hz 1.52) *tri-table*)))) - - -;; Single tick generators: - -(defun get-metronome-tick (hz gain) - (resample - (sound-srate-abs 44100 (metronome-tick hz gain)) - *sound-srate*)) - - -(defun get-ping (pitch ticklen) - (stretch-abs ticklen - (mult - (control-srate-abs *sound-srate* (pwl 0.005 amp 0.995 amp 1)) - (osc pitch)))) - - -(defun get-resonant-noise (pitch) - (stretch-abs 0.05 ; 50 milliseconds - (mult - (control-srate-abs *sound-srate* (pwl 0.05 amp 0.95 amp 1)) - (normalize (lowpass2 (noise 1) (step-to-hz pitch) 20))))) - - -(defun get-noise-click (pitch) - (stretch-abs 0.005 - (mult - (control-srate-abs *sound-srate* (pwl 0.005 amp 0.995 amp 1)) - (normalize (lowpass2 (noise 1) (step-to-hz pitch) 2))))) - - -(defun get-drip (pitch ticklen) - (stretch-abs ticklen - (mult - (control-srate-abs *sound-srate* (pwl 0.005 amp 0.995 amp 1)) - (normalize (drip (step-to-hz pitch)))))) - - -(defun get-cowbell (pitch) - (mult 0.8 (cowbell (step-to-hz pitch)))) - - -;; Make selected click -(defun click (accent) - (setq pitch (if (= accent 1) HIGH LOW)) - (setq amp (if (= accent 1) 0.75 0.5)) - (case CLICK-TYPE - (0 (get-metronome-tick (step-to-hz pitch) amp)) - (1 (get-ping pitch 0.01)) - (2 (get-ping pitch 0.08)) - (3 (get-cowbell pitch)) - (4 (get-resonant-noise pitch)) - (5 (get-noise-click pitch)) - (6 (get-drip pitch 0.007)) - (t (get-drip pitch 0.1)))) - - -(defun swing-adjust (i val) - (* val (/ 3.0) (rem (1+ i) 2))) - - -;Make one measure and save it in the global *measure* -(defun makemeasure () - (setf *measure* - (sim - (s-rest (* TIMESIG beatlen)) ;required for trailing silence - (click 1) ;accented beat - (simrep (count (- TIMESIG 1)) - (at-abs (* beatlen (+ count 1 (swing-adjust count SWING))) - (cue (click 0))))))) ;unaccented beat - - -(defun samplecount (total) - ;;; Return number of samples required to reach target - (defun lastsample (target) - (let ((required (- target total))) - (setf total target) - required)) - (function lastsample)) - - -(defun get-measure (barnum) - (let ((end (* (1+ barnum) (* TIMESIG beatlen))) - required-samples) - ;; Actual end time is integer samples - (setf end (round (* end *sound-srate*))) - (setf required-samples (funcall addsamples end)) - (setf *measure* (set-logical-stop (cue *measure*) - (/ required-samples *sound-srate*)))) - *measure*) - - -(defun make-click-track (barcount mdur) - (seqrep (i barcount) (cue (get-measure i)))) - - -;;;;;;;;;;;;;;;;; -;; MAIN PROGRAM -;;;;;;;;;;;;;;;;; - - -(setf beatlen (/ 60.0 TEMPO)) - -;call function to make one measure -(makemeasure) - -; If 'Number of bars' = 0, calculate bars from 'Rhythm track duration'. -(if (= BARS 0) - (setq barcount (/ CLICK-TRACK-DUR (* TIMESIG beatlen))) - (setf barcount BARS)) - -;if previewing, restrict number of bars -(let ((preview (/ (get '*project* 'preview-duration) - (* TIMESIG beatlen)))) - (if *previewp* - (setf barcount (min preview barcount)))) - -;round up number of bars -(setf barcount (round-up barcount)) - -;; Calculate LEN for progress bar. -(setf len (/ (* 60.0 *sound-srate* TIMESIG barcount) TEMPO)) - -;; Initialize sample count -(setf addsamples (samplecount 0)) - -(if (< barcount 1) - (format nil (_ "Set either 'Number of bars' or~%~ - 'Rhythm track duration' to greater than zero.")) - (if *previewp* - ;; Don't preview the offset (silence). - (make-click-track barcount (* TIMESIG beatlen)) - (seq (s-rest OFFSET) - (make-click-track barcount (* TIMESIG beatlen))))) diff --git a/Release/plug-ins/rissetdrum.ny b/Release/plug-ins/rissetdrum.ny deleted file mode 100644 index e6b1fe95a98050b4ca315675a6b882baad3c40b2..0000000000000000000000000000000000000000 --- a/Release/plug-ins/rissetdrum.ny +++ /dev/null @@ -1,82 +0,0 @@ -$nyquist plug-in -$version 4 -$type generate -$preview linear -$i18n-hint named for Jean-Claude Risset (silent t) -$name (_ "Risset Drum") -$debugbutton false -$author (_ "Steven Jones") -$release 2.3.0-2 -$copyright (_ "GNU General Public License v2.0 or later") - -;; rissetdrum.ny by Steven Jones, after Jean Claude Risset. -;; Updated by Steve Daulton 2012 and May 2015. - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control FREQ (_ "Frequency (Hz)") real "" 100 50 2000 -$control DECAY (_ "Decay (seconds)") real "" 2 0.1 60 -$control CF (_ "Center frequency of noise (Hz)") real "" 500 100 5000 -$control BW (_ "Width of noise band (Hz)") real "" 400 10 1000 -$control NOISE (_ "Amount of noise in mix (percent)") real "" 25 0 100 -$control GAIN (_ "Amplitude (0 - 1)") real "" 0.8 0 1 - - -;; Reduce length when previewing. -(setq pdur - (if *previewp* - (get '*project* 'preview-duration) - DECAY)) - - -(setq *rdrum-table* - (list - (mult 0.17 - (sum - (scale 1.00 (build-harmonic 10 2048)) - (scale 1.50 (build-harmonic 16 2048)) - (scale 2.00 (build-harmonic 22 2048)) - (scale 1.50 (build-harmonic 23 2048)))) - (hz-to-step 1) t)) - - -(defun log2 (n) - (/ (log (float n))(log 2.0))) - - -(defun percussion-env (dur) - (let* ((half-life (expt 2.0 (- (log2 dur) 3)))) - (exp-dec 0 half-life dur))) - - -(defun risset-drum () - (let* ((decay2 (* DECAY 0.50)) - (low-note (* FREQ 0.10)) - (tone-gain (- 1 NOISE))) - (setf pink (lowpass6 (noise decay2) BW)) - (setf rdrum - (mult tone-gain - (osc (hz-to-step low-note) decay2 *rdrum-table*))) - (setf noise-band - (mult NOISE - (sine (hz-to-step CF) decay2) - pink)) - (sum - (mult - (percussion-env decay2) - (sum noise-band rdrum )) - (mult tone-gain - (percussion-env DECAY) - (sine (hz-to-step FREQ) DECAY))))) - - -;; Generate and normalize -(let* ((output (risset-drum)) - (output (extract-abs 0 pdur output)) ; shorten if necessary for preview. - (peakval (peak output ny:all))) - (scale (/ GAIN peakval) output)) diff --git a/Release/plug-ins/rms.ny b/Release/plug-ins/rms.ny deleted file mode 100644 index a39d61382faa1e622e7fb228f874bfc1b0ba23ea..0000000000000000000000000000000000000000 --- a/Release/plug-ins/rms.ny +++ /dev/null @@ -1,67 +0,0 @@ -;nyquist plug-in -;version 4 -;type analyze -;name "Measure RMS" -;debugbutton false -;author "Steve Daulton" -;release 2.3.1-1 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - -;; This plug-in demonstrates how authors of Nyquist plug-ins may add translations -;; for output messages. It is not currently possible to provide translations for strings -;; in the header comments (such as the plug-in name) of 3rd party plug-ins. - - -;; Translations strings: -;; The "en" translation is not normally required unless the original text is in another -;; language, but it can make testing easier. -(setf *locale* - '(("en" (("Left" "Left") - ("Right" "Right") - ("Stereo" "Stereo") - ("Mono" "Mono") - ("dB" "dB"))) - ("de" (("Left" "Links") - ("Right" "Rechts") - ("Stereo" "Stereo") - ("Mono" "Mono") - ("dB" "dB"))) - ("es" (("Left" "Izquierda") - ("Right" "Derecha") - ("Stereo" "Estéreo") - ("Mono" "Mono") - ("dB" "dB"))) - ("fr" (("Left" "Gauche") - ("Right" "Droite") - ("Stereo" "Stéréo") - ("Mono" "Mono") - ("dB" "dB"))) - ("ru" (("Left" "Левый") - ("Right" "Правый") - ("Stereo" "Стерео") - ("Mono" "Моно") - ("dB" "дБ"))))) - - -(defun stereo-rms(ar) - ;;; Stereo RMS is the root mean of all (samples ^ 2) [both channels] - (let ((left-mean-sq (* (aref ar 0)(aref ar 0))) - (right-mean-sq (* (aref ar 1)(aref ar 1)))) - (sqrt (/ (+ left-mean-sq right-mean-sq) 2.0)))) - - -(let ((rms (get '*selection* 'rms))) - (if (arrayp rms) - (format nil "~a: \t~a ~a~%~ - ~a: \t~a ~a~%~ - ~a: \t~a ~a" - (_ "Left") (linear-to-db (aref rms 0)) (_ "dB") - (_ "Right") (linear-to-db (aref rms 1)) (_ "dB") - (_ "Stereo") (linear-to-db (stereo-rms rms)) (_ "dB")) - (format nil "~a: \t~a ~a" (_ "Mono")(linear-to-db rms)(_ "dB")))) diff --git a/Release/plug-ins/sample-data-export.ny b/Release/plug-ins/sample-data-export.ny deleted file mode 100644 index 0f173b86029392d7bc1a19697451f4172016b020..0000000000000000000000000000000000000000 --- a/Release/plug-ins/sample-data-export.ny +++ /dev/null @@ -1,510 +0,0 @@ -$nyquist plug-in -$version 4 -$type tool analyze -$name (_ "Sample Data Export") -$debugbutton false -$author (_ "Steve Daulton") -$release 3.0.4-2 -$copyright (_ "GNU General Public License v2.0 or later") - - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -;; To enable L/R prefix before alternate L/R channels -;; (text output with header only) -;; remove the semicolon from the start of the next line: -;(setq LR-PREFIX '("L: " "R: ")) - -$control NUMBER (_ "Limit output to first") int-text (_ "samples") 100 1 1000000 -$control UNITS (_ "Measurement scale") choice ((_ "dB") (_ "Linear")) 0 -$control FILENAME (_ "Export data to") file (_ "Select a file") "*default*/sample-data.txt" (((_ "Text file") (txt TXT)) - ((_ "CSV files") (csv CSV)) - ((_ "HTML files") (html HTML htm HTM)) - ((_ "All files") (""))) "save,overwrite" -$control FILEFORMAT (_ "Index (text files only)") choice ((_ "None") - ("Count" (_ "Sample Count")) - ("Time" (_ "Time Indexed"))) -$control HEADER (_ "Include header information") choice ((_ "None") - (_ "Minimal") - (_ "Standard") - (_ "All")) 0 -$control OPTEXT (_ "Optional header text") string "" "" -$control CHANNEL-LAYOUT (_ "Channel layout for stereo") choice (;i18n-hint: Left and Right - ("SameLine" (_ "L-R on Same Line")) - ("Alternate" (_ "Alternate Lines")) - ;i18n-hint: L for Left - ("LFirst" (_ "L Channel First"))) 0 -$control MESSAGES (_ "Show messages") choice ((_ "Yes") - ("Errors" (_ "Errors Only")) - (_ "None")) 0 - - -;; Global constants -(setf NUMBER (min (truncate len) NUMBER)) -(when (not (boundp 'LR-PREFIX))(setq LR-PREFIX nil)) -(setq *float-format* "%1.5f") ; 5 decimal places - - -;;; Return file extension or empty string -(defun get-extension (fname) - (let ((n (1- (length fname))) - (ext "")) - (do ((i n (1- i))) - ((= i 0) ext) - (when (char= (char fname i) #\.) - (setf ext (subseq fname (1+ i))) - (return ext))))) - - -;;; stereo peak -(defun stereomax (snd) - (if (arrayp *track*) - (max (peak (aref *track* 0) NUMBER) - (peak (aref *track* 1) NUMBER)) - (peak *track* NUMBER))) - - -;;; stereo rms -(defun srms (snd) - (if (arrayp snd) - (let* ((sql (mult (aref *track* 0)(aref *track* 0))) - (sqr (mult (aref *track* 1)(aref *track* 1))) - (avgsq (mult 0.5 (sum sql sqr))) - (avgsq (snd-avg avgsq NUMBER NUMBER op-average))) - (lin-to-db (peak (snd-sqrt avgsq) 1))) - (let* ((sndsq (mult snd snd)) - (avgsq (snd-avg sndsq NUMBER NUMBER op-average))) - (lin-to-db (peak (snd-sqrt avgsq) 1))))) - - -;;; DC off-set mono -(defun dc-off-mon (sig len) - (let* ((total 0) - (sig (snd-copy sig)) - (ln (truncate len))) - (dotimes (num ln) - (setq total (+ total (snd-fetch sig)))) - (/ total (float len)))) - - -;;; DC offset (mono/stereo) -(defun dc-off (sig) - (if (arrayp sig) - (let ((lin0 (dc-off-mon (aref sig 0) NUMBER)) - (lin1 (dc-off-mon (aref sig 1) NUMBER))) - (list lin0 (lin-to-db (abs lin0)) lin1 (lin-to-db (abs lin1)))) - (let ((lin (dc-off-mon sig NUMBER))) - (list lin (lin-to-db (abs lin)))))) - - -;;; Platform independent representation of negative infinity -(defun lin-to-db (val) - (if (= val 0) - ;i18n-hint abbreviates negative infinity - (_ "[-inf]") - (linear-to-db val))) - - -;;; Get sample and convert to dB if required -(defun snd-get (snd &optional (dB 0)) - (if (= dB 0) ; dB scale - (lin-to-db (abs (snd-fetch snd))) - (snd-fetch snd))) ; linear scale - - -;; FILEFORMAT 0=Text List, 1=Sample count index, 2=Time index, 3=CSV, -;; (4=html but not used here). -;; Optional 'same' [line] argument is either 'true' or 'nil' -(defun formatprint (val snd &optional same) - (case FILEFORMAT - (0 (format fp "~a~a" ; plain list - (snd-get snd UNITS) - (if same "\t" "\n"))) - (1 (format fp "~a\t~a~a" ; count index - val - (snd-get snd UNITS) - (if same "\t" "\n"))) - (2 (format fp "~a\t~a~a" ; time index - (/ (1- val) *sound-srate*) - (snd-get snd UNITS) - (if same "\t" "\n"))) - (3 (format fp "~a~a" ; csv - (snd-get snd UNITS) - (if (or (= CHANNEL-LAYOUT 2) same) "," "\n"))))) - - -;;; Print sample data to file -(defun print-text (sig) - (do ((n 1 (1+ n))) - ((> n NUMBER)) - (if (arrayp sig) ; Stereo (alternate lines) - (progn - ;; option to prefix alternate lines with L/R - (when LR-PREFIX - (unless (or (= HEADER 0)(= FILEFORMAT 3)) - (format fp "~a" (first LR-PREFIX)))) - (if (= CHANNEL-LAYOUT 0) ; IF 'Same Line' then "True" - (formatprint n (aref sig 0) T) - (formatprint n (aref sig 0))) - (when LR-PREFIX - (unless (or (= HEADER 0)(= FILEFORMAT 3)) - (format fp "~a" (second LR-PREFIX)))) - (formatprint n (aref sig 1))) - (formatprint n sig)))) - - -;; Print to file -(defun printdata () - (case HEADER - (0 (format t (normhead))(format fp (nohead))) - (1 (format t (normhead))(format fp (minhead))) - (2 (format t (normhead))(format fp (normhead))) - (3 (format t (normhead))(format fp (fullhead)))) - (if (and (arrayp *track*)(= CHANNEL-LAYOUT 2)) - ;; Stereo and left channel first - (progn - (unless (= HEADER 0) ; Don't print 'channel' if no header - (format fp (_ "Left Channel.~%~%"))) - (print-text (aref *track* 0)) - (if (= HEADER 0) ; Don't print 'channel' if no header - (format fp "~%") - (format fp (_ "~%~%Right Channel.~%~%"))) - (print-text (aref *track* 1))) - ;; mono or alternate - (print-text *track*)) - (close fp) - (if (= MESSAGES 0) - (format nil (_ "~aData written to:~%~a") (normhead) FILENAME) - (progn - (format t (_ "~aData written to:~%~a") (normhead) FILENAME) - ""))) - - -;;; Header text - -(defun nohead () - (if (> (length OPTEXT) 0) - (format nil "~a~%~a~%" - OPTEXT - (get 'info 'chan-order)) - "")) - - -(defun minhead () - (format nil (_ "Sample Rate: ~a Hz. Sample values on ~a scale.~%~a~%~a") - (get 'info 'srate) ; sample rate - (get 'info 'units) ; units - (get 'info 'chan-order) ; Channel Order - (if (> (length OPTEXT) 0) - (format nil "~a~%~%~%" OPTEXT) ; optional text - (format nil "~%")))) ; no optional text - - -(defun normhead () - (if (= FILEFORMAT 4) ; html - (format nil (_ "~a ~a~%~aSample Rate: ~a Hz.~%Length processed: ~a samples ~a seconds.~a") - FILENAME ; file name - (get 'info 'channels) ; mono/stereo - (get 'info 'chan-order) ; Channel Order - (get 'info 'srate) ; sample rate - NUMBER ; number of samples - (get 'info 'duration) ; duration (seconds) - (if (> (length OPTEXT)0) - (format nil "~%~a~%~%" OPTEXT) ; optional text - (format nil "~%~%"))) ; no optional text - (format nil (_ "~a ~a~%~aSample Rate: ~a Hz. Sample values on ~a scale.~%~ - Length processed: ~a samples ~a seconds.~a") - FILENAME ; file name - (get 'info 'channels) ; mono/stereo - (get 'info 'chan-order) ; Channel Order - (get 'info 'srate) ; sample rate - (get 'info 'units) ; units - NUMBER ; number of samples - (get 'info 'duration) ; duration (seconds) - (if (> (length OPTEXT)0) - (format nil "~%~a~%~%" OPTEXT) ; optional text - (format nil "~%~%"))))) ; no optional text - - -(defun fullhead () - (format nil (_ "~a~%Sample Rate: ~a Hz. Sample values on ~a scale. ~a.~%~aLength processed: ~a ~ - samples, ~a seconds.~%Peak amplitude: ~a (linear) ~a dB. Unweighted RMS: ~a dB.~%~ - DC offset: ~a~a") - FILENAME ; file name - (get 'info 'srate) ; sample rate - (get 'info 'units) ; units - (get 'info 'channels) ; mono/stereo - (get 'info 'chan-order) ; Channel Order - NUMBER ; number of samples - (get 'info 'duration) ; duration (seconds) - (setq smax (stereomax *track*)) ; peak amplitude linear - (lin-to-db smax) ; peak amplitude dB - (srms *track*) ; rms - (let ((vals (dc-off *track*))) ; DC offset - (if (= (length vals) 2) ; mono - (format nil (_ "~a linear, ~a dB.") - (first vals) (second vals)) - (format nil (_ "Left: ~a lin, ~a dB | Right: ~a lin, ~a dB.") - (first vals) (second vals) (third vals) (fourth vals)))) - (if (> (length OPTEXT)0) - (format nil "~%~a~%~%~%" OPTEXT) ; optional text - (format nil "~%~%~%")))) ; no optional text - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; HTML Output ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun html-head () (strcat -"<!DOCTYPE html> -<html> -<head> -<meta name=\"generator\" content= -\"Sample Data Export by Steve Daulton, (https://www.audionyq.com). Released under GPL v2.0+\"> -<meta name=\"description\" content=\"Sample Printer, Free Audacity plug-in\" /> -<meta name=\"keywords\" content=\"sample printer,Audacity,plug-ins,plugins,effects,audio,audio processing,music,analyze\" /> -<meta name=\"author\" content=\"Steve Daulton\" /> -<meta charset=\"UTF-8\"> - -<style type=\"text/css\"> -body { - margin: 1em 5%; - background-color: #dda; - font-family:Arial,Helvetica,sans-serif; - } -table,th,td { - background-color: #fff; - border:1px solid black; - text-align: center; -} -table { - width: auto; - border: 2px; - border-style:ridge; - border-collapse:collapse; -} -td { - text-align: right; - padding-right: 0.5em; -} -tr:hover td { - background-color:#fcd; -} -th { - padding: 0 0.5em; - background-color: #ddf; - border-bottom-width: 2px; - border-bottom-style:ridge; -} -h1 { - font-size: 1.6em; - color: #633; -} -h2 { - font-size: 1.4em; - color: #633; -} -h3 { - font-size: 1em; - color: #366; -} -h4 { - font-size: 1em; - color: #000; -} -ul { - position:relative; - top: -0.5em; - } -#footer { - font-size: 0.8em; - position:relative; - top: 0.5em; - left: 2%; - } -#footer span { - font-style:italic; - font-weight: bold; - color: #633; - } -#footer a:link,a:visited { - color: #639; - text-decoration: none; - } -#footer a:hover,a:active { - text-decoration: underline; - color: blue; - } -</style> -<title>" (_ "Sample Data Export") "</title> -</head> -")) - - -;;; document headings -(defun doc-head () - (format nil -(strcat "<body> -<h1>" (_ "Sample Data Export") " - ~a</h1> -~a -<h4>~a. " (_ "~a samples.") " " (_ "~a seconds.") "<br></h4> -<h3>" (_ "Audio data analysis:") "</h3> -<ul> -<li>" (_ "<b>Sample Rate:</b> ~a Hz.") "</li>" -; i18n-hint: abbreviates "decibels" -"<li>" (_ "<b>Peak Amplitude:</b> ~a (linear) ~a dB.") "</li>" -; i18n-hint: RMS abbreviates root-mean-square, a method of averaging a signal; there also "weighted" versions of it but this isn't that -"<li>" (_ "<b>RMS</b> (unweighted): ~a dB.") "</li>" -; i18n-hint: DC derives from "direct current" in electronics, really means the zero frequency component of a signal -"<li>" (_ "<b>DC Offset:</b> ~a") "</li> -</ul> -") ; end concatenated format string with inserted translations - (string-right-trim ".html" FILENAME) - (format nil "<h2>~a</h2>" OPTEXT) ; Optional heading - (get 'info 'channels) ; mono/stereo - NUMBER ; number of samples - (get 'info 'duration) ; duration (seconds) - (get 'info 'srate) ; sample rate - (setq smax (stereomax *track*)) ; peak amplitude linear - (lin-to-db smax) ; peak amplitude dB - (srms *track*) ; rms - (let ((vals (dc-off *track*))) ; DC offset - (if (= (length vals) 2) ; mono - (format nil (_ "~a linear, ~a dB.") - (first vals)(second vals)) - (format nil (_ "Left: ~a lin, ~a dB | Right: ~a linear, ~a dB.") - (first vals)(second vals)(third vals)(fourth vals)))))) - - -;;; table headings (mono) -(defun table-head-mono () -(strcat "<table title=\"" (_ "sample data") "\"> -<tr> -<th>" (_ "Sample #") "</th> -<th>" (_ "Seconds") "</th> -<th>" (_ "Value (linear)") "</th> -<th>" (_ "Value (dB)") "</th> -</tr>")) - - -;;; table headings (stereo) -(defun table-head-stereo () -(strcat "<table title=\"" (_ "audio sample value analysis") "\"> -<tr> -<th>" (_ "Sample #") "</th> -<th>" (_ "Seconds") "</th> -<th>" (_ "Left (linear)") "</th> -<th>" (_ "Right (linear)") "</th> -<th>" (_ "Left (dB)") "</th> -<th>" (_ "Right (dB)") "</th> -</tr>")) - - -(defun html-foot () - (format nil (strcat -"</table> -<p id=\"footer\">" (_ "Produced with <span>Sample Data Export</span> for -<a href=\"~a\">Audacity</a> by Steve -Daulton") " (<a href= -\"https://audionyq.com\">audionyq.com</a>)</p> -</body> -</html>") "https://www.audacityteam.org/")) - - -;;; html generator -(defun make-htm (id val1 &optional val2) - (if val2 - ;; stereo - (let ((time (/ (1- id) *sound-srate*)) - (db1 (lin-to-db (abs val1))) - (db2 (lin-to-db (abs val2)))) - (format fp - "<tr>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%~ - <td>~a</td>~%<td>~a</td>~%</tr>~%" - id time val1 val2 db1 db2)) - ;; mono - (let ((time (/ (1- id) *sound-srate*)) - (db (lin-to-db (abs val1)))) - (format fp - "<tr>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%<td>~a</td>~%</tr>~%" - id time val1 db)))) - - -(defun printhtml () - (format fp (html-head)) - (format fp (doc-head)) - (if (arrayp *track*) - (progn - (format fp (table-head-stereo)) - (do ((i 1 (1+ i))) - ((> i NUMBER)) - (make-htm i - (snd-fetch (aref *track* 0)) - (snd-fetch (aref *track* 1))))) - (progn - (format fp (table-head-mono)) - (do ((i 1 (1+ i))) - ((> i NUMBER)) - (make-htm i (snd-fetch *track*))))) - (format fp (html-foot)) - (close fp) - (if (= MESSAGES 0) - (format nil (_ "~aData written to:~%~a") (normhead) FILENAME) - (progn - (format t (_ "~aData written to:~%~a") (normhead) FILENAME) - ""))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; END OF HTML ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;;; basic info for headers -(defun put-head-info () - (putprop 'info (truncate *sound-srate*) 'srate) - (putprop 'info (if (= UNITS 0) (_ "dB") (_ "linear")) 'units) - (putprop 'info (/ NUMBER *sound-srate*) 'duration) - (putprop 'info - (if (arrayp *track*) - (_ "2 channels (stereo)") (_ "1 channel (mono)")) - 'channels) - ;; stereo sample order - (putprop 'info - (cond - ((and (= FILEFORMAT 3)(= CHANNEL-LAYOUT 0)) ; csv, channel in column - (format nil (_ "One column per channel.~%"))) - ((and (= FILEFORMAT 3)(= CHANNEL-LAYOUT 2)) ; csv, channel in row - (format nil (_ "One row per channel.~%"))) - ((or (soundp *track*)(= FILEFORMAT 4)) ; mono sound or HTML - "") - ((= CHANNEL-LAYOUT 0) (format nil (_ "Left channel then Right channel on same line.~%"))) - ((= CHANNEL-LAYOUT 1) (format nil (_ "Left and right channels on alternate lines.~%"))) - ((= CHANNEL-LAYOUT 2) (format nil (_ "Left channel first then right channel.~%"))) - (T (_ "Unspecified channel order"))) - 'chan-order)) - - -;;; Specifying a CSV or HTML file overrides the (text only) format selection. -(let ((file-extension (get-extension FILENAME))) - (cond - ((string-equal file-extension "csv") - (setf FILEFORMAT 3)) - ((string-equal file-extension "html") - (setf FILEFORMAT 4)) - ((string-equal file-extension "htm") - (setf FILEFORMAT 4)))) - - -(setq fp (open FILENAME :direction :output)) -(cond - (fp (put-head-info) - (if (= FILEFORMAT 4) - (printhtml) ; html output - (printdata))) ; text output - (t (if (= MESSAGES 2) - (format t (_ "Error.~%\"~a\" cannot be written.") FILENAME) - (format nil (_ "Error.~%\"~a\" cannot be written.") FILENAME)))) diff --git a/Release/plug-ins/sample-data-import.ny b/Release/plug-ins/sample-data-import.ny deleted file mode 100644 index beb699f327f00b5eff0f9b2ad9df468d4181aeb5..0000000000000000000000000000000000000000 --- a/Release/plug-ins/sample-data-import.ny +++ /dev/null @@ -1,107 +0,0 @@ -$nyquist plug-in -$version 4 -$type tool generate -$name (_ "Sample Data Import") -$debugbutton false -$author (_ "Steve Daulton") -$release 3.0.4-1 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control FILENAME (_ "Select file") file "" "*default*/sample-data.txt" (((_ "Text file") (txt TXT)) - ((_ "All files") (""))) "open,exists" -$control BAD-DATA (_ "Invalid data handling") choice (("ThrowError" (_ "Throw Error")) - ("ReadAsZero" (_ "Read as Zero"))) 0 - - -;; Check file can be opened -(defun fileopensp (fname) - (cond - ((not (setf fstream (open fname))) - (throw 'err (format nil (_ "Error~%~ - '~a' could not be opened.~%~ - Check that file exists.") - fname))) - ; File opened OK, so check for normal ASCII, then close it and return 'true' - (t (do ((j 0 (1+ j))(b (read-byte fstream)(read-byte fstream))) - ((or (> j 100000)(not b))) - (when (> b 127) - (throw 'err (format nil (_ "Error:~%~ - The file must contain only plain ASCII text.~%~ - (Invalid byte '~a' at byte number: ~a)") b (1+ j) )))) - (close fstream) - t))) - -;; ':new' creates a new class 'streamreader' -;; 'filestream' and 'channel' are its instance variables. -;; (every object of class 'streamreader' has its own -;; copy of these variables) -(setq streamreader - (send class :new '(filestream channel))) - -;; Initialize class 'streamreader' -(send streamreader :answer :isnew '(stream ch) '( - (setq filestream stream) - (setq channel ch))) - -;; Create ':next' method. -;; Snd-fromobject calls this method to obtain the -;; next sound sample until it receives 'nil' -(send streamreader :answer :next '() '( - (case channel - (0 ;mono - (read-and-verify filestream)) - (1 ;left channel - ;Note, we still need to verify data even if skipping it. - (let ((val (read-and-verify filestream))) - (read-and-verify filestream) ;skip right channel sample - val)) - (t ;right channel - (read-and-verify filestream) ;skip left channel sample - (read-and-verify filestream))))) - -(defun read-and-verify (stream) -"snd-fromobject requires float values, nil to terminate" - (let ((val (read stream))) - (cond - ((not val) nil) ;end of file - ((numberp val) (float val)) ;valid. - ((= BAD-DATA 0) ;invalid. Throw error and quit - (throw 'err (format nil (_ "Error~%~ - Data must be numbers in plain ASCII text.~%~ - '~a' is not a numeric value.") val))) - (t 0.0)))) ;invalid. Replace with zero. - -;; Instantiate a new sound object -(defun make-sound-object (stream chan) - (send streamreader :new stream chan)) - -(defun sound-from-file () - ;; Set path. fileopenp should return 'true' - (if (not (fileopensp FILENAME)) - (throw 'err (format nil (_ "Error.~%Unable to open file")))) - ; Note: we can't use (arrayp *track*) because - ; *track* is nil in generate type plug-ins. - (cond - ((= (get '*track* 'channels) 2) - (let ((left-snd (get-sound FILENAME 1)) - (right-snd (get-sound FILENAME 2))) - (vector left-snd right-snd))) - (t ;; Mono track - (get-sound FILENAME 0)))) - -(defun get-sound (fname chan) - (let* ((stream (open fname :direction :input)) - (left (make-sound-object stream chan))) - (setf audio-out (snd-fromobject 0 *sound-srate* left)) - (snd-play audio-out) ;force samples to be calculated now. - (close stream) - audio-out)) - -(catch 'err (sound-from-file)) diff --git a/Release/plug-ins/spectral-delete.ny b/Release/plug-ins/spectral-delete.ny deleted file mode 100644 index befe4af254a283c348098cdf297daf2214ed93cf..0000000000000000000000000000000000000000 --- a/Release/plug-ins/spectral-delete.ny +++ /dev/null @@ -1,136 +0,0 @@ -$nyquist plug-in -$version 4 -$type process spectral -$name (_ "Spectral Delete") -$author (_ "Steve Daulton") -$release 3.0.4-1 -$copyright (_ "GNU General Public License v2.0 or later") - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -(defun sinc (x fc) - ;; http://www.dspguide.com/ch16/1.htm - ;; Note that fc is a fraction of the sample rate. - (if (= x 0) - (* 2 pi fc) - (/ (sin (* 2 pi fc x)) x))) - -(defun blackman (i M) - ;; Where: 0 <= i <= M - (+ 0.42 - (* -0.5 (cos (/ (* 2.0 pi i) M))) - (* 0.08 (cos (/ (* 4 pi i) M))))) - -(defun calc-kernel (size fc) - ;; Generate windowed sinc kernel impulse - (when (oddp size) - (error "Size of sinc filter must be even")) - (let ((ar (make-array (1+ size))) - (norm 0) ;Normalization factor - val) - (do ((i 0 (1+ i)) - (j size (1- j)) - (x (- halfk) (1+ x))) - ((> i j)) - (setf val (* (sinc x fc)(blackman i size))) - (setf norm (+ norm val)) - (setf (aref ar i) val) - (setf (aref ar j) val)) - ;; norm is sum of all samples, but don't count middle value twice. - (setf norm (- (* norm 2)(aref ar halfk))) - (dotimes (i size ar) - (setf (aref ar i)(/ (aref ar i) norm))))) - -(defun get-kernel (size fc type) - ;; type: 0 (low pass) or 1 (highpass) - ;; Returns filter kernel as a sound. - (let ((kernel (calc-kernel size fc))) - (when (= type 1) - ;; Convert kernel to high pass - ;; https://tomroelandts.com/articles/how-to-create-a-simple-high-pass-filter - (dotimes (i size kernel) - (setf (aref kernel i)(* -1 (aref kernel i)))) - (incf (aref kernel halfk))) - (snd-from-array 0 *sound-srate* kernel))) - -(defun sinc-filter (sig start end impulse) - (extract-abs start end (convolve sig impulse))) - -(defmacro validate-low-hz (hz fmin fmax) - ;; Discard if out of valid range. - ;; Do NOT coerce into range if too high - if multiple tracks with - ;; different sample rates, that could cause very unexpected results. - `(if (or (not ,hz) (< ,hz fmin) (> ,hz fmax)) - (setf ,hz nil))) - -(defmacro validate-high-hz (hz fmin fmax) - ;; Discard if too high. Coerce into range if too low. - `(if (or (not ,hz) (>= ,hz fmax)) - (setf ,hz nil) - (setf ,hz (max ,hz fmin)))) - -(defun dofilter (cf bw type) - ;; type: 0 (low pass) or 1 (highpass) - ;; Calculate kernel length (must be even) - ;; http://www.dspguide.com/ch16/2.htm - (setf klength (/ 4.0 bw)) - (setf halfk (round (/ klength 2))) - (setf klength (* 2 halfk)) - (let ((imp (get-kernel klength cf type)) - (start (/ halfk *sound-srate*)) - (dur (get-duration 1))) - (multichan-expand #'sinc-filter *track* start (+ start dur) imp))) - -(defun bandwidth (hz) - ;; Set bandwidth ratio of each filter as 1% of filter frequency. - (* hz 0.01)) - -(defun bw-ratio (hz) - ;; Bandwidth ratio is required as a fraction of the sampling rate - (/ (bandwidth hz) *sound-srate*)) - -(defun filter () - (when (< *sound-srate* 100) - (throw 'err (_ "Error.~%Track sample rate below 100 Hz is not supported."))) - (let* ((f0 (get '*selection* 'low-hz)) - (f1 (get '*selection* 'high-hz)) - (fc (get '*selection* 'center-hz)) - ; If frequency too low, filter length is too large. - (fmin (* 0.002 *sound-srate*)) - (fmax (* 0.498 *sound-srate*)) - (tn (truncate len)) - (transition (truncate (* 0.01 *sound-srate*))) ; 10 ms - (t1 (min transition (/ tn 2))) ; fade in length (samples) - (t2 (max (- tn transition) (/ tn 2))) ; length before fade out (samples) - (breakpoints (list t1 1.0 t2 1.0 tn)) - (env (snd-pwl 0.0 *sound-srate* breakpoints))) - (validate-low-hz f0 fmin fmax) - (validate-high-hz f1 fmin fmax) - ;; Handle very narrow selections. - ;; This may cause f0 or f1 to 'slightly' exceed fmin fmax. - (when (and f0 f1 (< (- f1 f0) (* fc 0.02))) - (setf f0 (* fc 0.99)) - (setf f1 (* fc 1.01))) - (when f0 - (setf lp-width (bw-ratio f0)) - (setf f0 (/ f0 *sound-srate*))) - (when f1 - (setf hp-width (bw-ratio f1)) - (setf f1 (/ f1 *sound-srate*))) - ;(format t "Low: ~a High: ~a" (if f0 (* f0 *sound-srate*) nil) (if f1 (* f1 *sound-srate*) nil)) - (if (not (or f0 f1)) - "" ;may occur if multiple tracks with different sample rates - (sim - (mult env - (if f0 (dofilter f0 lp-width 0) 0)) - (mult env - (if f1 (dofilter f1 hp-width 1) 0)) - (mult (diff 1.0 env) *track*))))) - - -(catch 'err (filter)) diff --git a/Release/plug-ins/tremolo.ny b/Release/plug-ins/tremolo.ny deleted file mode 100644 index d0ee70e2522da8d8b974e24e0e374c8326f8f80c..0000000000000000000000000000000000000000 --- a/Release/plug-ins/tremolo.ny +++ /dev/null @@ -1,57 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview linear -$name (_ "Tremolo") -$debugbutton disabled -$author (_ "Steve Daulton") -$release 2.4.0 -$copyright (_ "GNU General Public License v2.0 or later") - -;; tremolo.ny by Steve Daulton (www.easyspacepro.com) July 2012. -;; Based on Tremolo by Dominic Mazzoni and David R. Sky." - -;; License: GPL v2+ -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control WAVE (_ "Waveform type") choice ((_ "Sine") - (_ "Triangle" "waveform") - (_ "Sawtooth") - ("InverseSawtooth" (_ "Inverse Sawtooth")) - (_ "Square")) 0 - -$control PHASE (_ "Starting phase (degrees)") int "" 0 -180 180 -$control WET (_ "Wet level (percent)") int "" 40 1 100 -$control LFO (_ "Frequency (Hz)") float-text "" 4 0.001 1000 - - -; set tremolo waveform -(setf waveform - (abs-env - (case WAVE - (0 *sine-table*) - (1 *tri-table*) - ; sawtooth - (2 (maketable (pwlv -1 0.995 1 1 -1))) - ; inverse sawtooth - (3 (maketable (pwlv -1 0.005 1 1 -1))) - ; square - (4 (maketable (pwlv -1 0.005 1 0.5 1 0.505 -1 1 -1)))))) - - -;;; Generate modulation wave -(defun mod-wave (level) - ; *sine-table* is 90 degrees rotated compared to other tables. - (if (= WAVE 0) - (setf phase-shift (- PHASE 90)) - (setf phase-shift PHASE)) - (sum (- 1 level) - (mult level - (osc (hz-to-step LFO) 1.0 waveform phase-shift)))) - - -(mult *track* (mod-wave (/ WET 200.0))) diff --git a/Release/plug-ins/vocoder.ny b/Release/plug-ins/vocoder.ny deleted file mode 100644 index 6dac03651c05b923499b39b0f512b05efde9d5dd..0000000000000000000000000000000000000000 --- a/Release/plug-ins/vocoder.ny +++ /dev/null @@ -1,117 +0,0 @@ -$nyquist plug-in -$version 4 -$type process -$preview enabled -$name (_ "Vocoder") -$debugbutton false -$author (_ "Edgar-RFT and Steve Daulton") -$release 3.1.2-1 -$copyright (_ "GNU General Public License v2.0") - - -;; If selected track is mono, the vocoder uses sine waves as the modulation -;; carrier, mixed with noise and radar needles according to slider settings. -;; If selected track is stereo, the right channel is used as the carrier wave. - -;; License: GPL v2 -;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -;; -;; For information about writing and modifying Nyquist plug-ins: -;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference - - -$control DST (_ "Distance: (1 to 120, default = 20)") float "" 20 1 120 -$control MST (_ "Output choice") choice (("BothChannels" (_ "Both Channels")) - ("RightOnly" (_ "Right Only"))) 0 -$control BANDS (_ "Number of vocoder bands") int "" 40 10 240 -$control TRACK-VL (_ "Amplitude of carrier wave (percent)") float "" 100 0 100 -$control NOISE-VL (_ "Amplitude of white noise (percent)") float "" 0 0 100 -$control RADAR-VL (_ "Amplitude of Radar Needles (percent)") float "" 0 0 100 -$control RADAR-F (_ "Frequency of Radar Needles (Hz)") float "" 30 1 100 - - -;; Return log to base 2 of x. -(defun log2 (x) - (/ (log (float x)) (log 2.0))) - - -;; Global constants. -;; Scale slider values for better control. -(setf TRACK-VOL (sqrt (/ TRACK-VL 100.0))) -(setf NOISE-VOL (expt (/ NOISE-VL 100.0) 2.0)) -(setf RADAR-VOL (sqrt (/ RADAR-VL 100.0))) - -;; number of octaves from 20 Hz. -;; Maximum number of octaves is: log2(high-hz / low-hz) -;; "2.205" is for compatibility with older versions of vocoder effect. -(setf OCTAVES (log2 (/ (/ *sound-srate* 2.205) 20))) - -;; interval - number of semitones per vocoder band -(setf INTERVAL (/ (* OCTAVES 12.0) BANDS)) - - -(defun make-radar-table (hz) - (let ((one (/ *sound-srate*)) ;one sample period - radar-table) - (setf radar-table - (stretch-abs 1 (sim (snd-const 1 one *sound-srate* one) - (s-rest (/ 1.0 hz))))) - (list radar-table (hz-to-step hz) T))) - - -;;; The Mixer -(defun mix-noise (sig) - (sum (cond ((= TRACK-VOL 0) 0) - ((< TRACK-VOL 1) (mult TRACK-VOL sig)) - (t sig)) - (if (> RADAR-VL 0) - (let ((r-table (make-radar-table RADAR-F))) - (mult RADAR-VOL - (osc (hz-to-step RADAR-F) 1 r-table))) - 0) - (if (> NOISE-VL 0) - (mult NOISE-VOL (noise 1)) - 0))) - - -;; Raise 'hz' by 'INTERVAL' semitones. -(defmacro next-hz (hz INTERVAL) - `(let* ((prev-step (hz-to-step ,hz)) - (next-step (+ prev-step ,INTERVAL))) - (step-to-hz next-step))) - - -(defmacro sumto (x y) - `(setf ,x (sum ,x ,y))) - - -;;; Stereo Vocoder - returns mono sound. -(defun vocoder (sig is-mono-track) - (let (mod-envelope - band - (result 0)) - (do ((i 0 (1+ i)) - (q (/ (sqrt 2.0) (/ OCTAVES BANDS))) ; quick approximation of q - (f (next-hz 20 (/ INTERVAL 2.0)) - (next-hz f INTERVAL))) - ((= i BANDS) result) - (when is-mono-track - (sumto (aref sig 1) (mult 0.5 (/ TRACK-VOL BANDS) (hzosc f)))) - (setf band (bandpass2 sig f q)) ; intermediate results (2 channels) - (setf mod-envelope (lowpass8 (s-abs (aref band 0)) (/ f DST))) - (sumto result (bandpass2 (mult mod-envelope (aref band 1)) f q))))) - - -;;; The Program -(if (= (+ TRACK-VOL NOISE-VOL RADAR-VOL) 0) - (format nil (_ "Error.~%No modulation carrier.")) - (progn - (if (arrayp *track*) - (setf sig (vector (aref *track* 0) (mix-noise (aref *track* 1)))) - (setf sig (vector *track* (mix-noise (s-rest 0))))) - (setf sig (vocoder sig (soundp *track*))) - ;; Normalize *track* to 0 db peak based on first 10 million samples. - (setf sig (scale (/ (peak sig 10000000)) sig)) - (if (or MST (soundp *track*)) - sig - (vector (aref *track* 0) sig))))