Portable (load "somelib.lsp") -- why not?

Wow, will version 4 let me inspect sound samples in the track outside of the selection? I have posted to the wish list and I wanted that capability for my very first Nyquist project, though I found another workaround.

Can you point me to a page telling me exactly what to expect for 4.0?

I am contemplating my own radical but compatible rewrite of the XLisp folder for a faster and more fully featured Lisp (but still much less than all of Common Lisp), beyond a few little fixes. This would be my own hobby project of possible interest on this board. Perhaps I would start my own Sourceforge project, choose a name that does not suggest any endorsement by the Audacity or Nyquist projects, and instruct developers how to drop the replacement into Audacity builds and use at their own risk. Just for the fun and self education of it. XLisp 2.0 is not so big that this would be an unmanageble fun project.

Does that sound too presumptuous? But then Audacity is “our” name here.

If you have a drop-box account, PM the necessary details to me so that I can share what I’ve got so far.

I appreciate that working on an existing project that has low activity can be frustrating, especially when the project maintainer has limited availability, but pushing bug fixes and enhancements upstream is likely to be more beneficial for Audacity users / Audacity plug-in writers, and much more widely used than a forked project.
I see no harm in running your own “hobby project”, but if it really is “compatible” then there would be no reason for not merging your bug fixes / enhancements back into CMU Nyquist would there?

That might be the desirable outcome, but before that happens I would need more freedom to develop and share it at my own pace, and other volunteers must try it to build confidence in it. Why should Roger or anyone trust my project before that?

Take all the time that you need :slight_smile:

If you write drop-in replacements for existing lsp files then I’m sure that Robert and I can test them.
If your changes require building from source, then I can do some testing as long as your patches are against the current svn head/

Going back to your original question:

It should not be a problem as long as the files to be loaded are placed in the Nyquist folder.
On Windows that is a subdirectory of the Audacity installation folder.
On Linux that is /usr/local/share/audacity/nyquist or /usr/share/audacity/nyquist
On Mac OS X, I think it will usually be under “Applications” (but check with a Mac user).
If the file is in the Nyquist folder then it can be loaded without specifying a path:

(load "filename")

“filename” is case sensitive on Linux and may be case sensitive on Mac OS X.
It is safest to include the file extension, though XLisp should assume “.lsp” if not specified, but I’ve not tested how well behaved that is.

It would probably not require much of a hack to allow the plug-ins folder to be used as an alternative to the Nyquist folder. That would be a lot more convenient on Linux.

There’s a forum member that seems to have a Common Lisp background.
I initially hoped for some input from this side.
https://forum.audacityteam.org/t/script-driven-sound-file-mixing/29737/3

Oh, so all is welk if new .lsp files go to nyquist? Somehow I misremembered things as not so simple or portable.

More amusing with an “h” :slight_smile:

It would be really nice if we could just put all the files into the Plug-ins folder, but that is the bit that has cross platform problems.

I have found a simple all-lisp solution. Add these lines to the end of init.lsp:

(setq *nyquist-home* (current-path))

(defun parent-path (path)
  (let ((path-length (length path)))
    (dotimes (ii (1- path-length) "")
      (when (equal (char path (- path-length ii 2)) *file-separator*)
        ;; return path that includes trailing separator
        (return (subseq path 0 (- path-length ii 1)))))))

(defun include (&rest relative-path-components)
  (let ((separator (string *file-separator*))
        (name (parent-path *nyquist-home*)))
    (when relative-path-components
      (setf name (strcat name (car relative-path-components)))
      (dolist (component (cdr relative-path-components))
        (setf name (strcat name separator component))))
    (load name)))

Now in a .ny file in plug-ins, this works to load a file in the same folder:

(include "plug-ins" "hello.lsp")

The file just contains (print “hello”) and I verified it with debug output of the plug in. (A message identifying the file also prints to debug whenever a file successfully opens for load, but oddly, no error message is printed when a nonexistent file path is given to load.)

We can argue about the best names and interface, I don’t pretend these are the best, but look, it works for me on Windows, with no mention of the unportable "" character or of absolute paths. Please verify on your system.

(Maybe the function could just work relative to nyquist-home instead and allow unix-y strings like “…/hello.lsp” which could be parsed in Lisp so as to work with Windows too: interpret each “…” as a call of parent-path, and substitute file-separator for each / character. Unix-y bias in the naming convention but everything is there to make it work portably.)

This solution relies on (current-path) and file-separator which are not defined in xlisp proper but in all the things that nyinit.lsp loads. (current-path) uses loadingfiles which is part of xlisp. The global loadingfiles is a list of alternating file names and file objects for the current nested (load). I discovered that when you execute a Nyquist plug-in, you are not inside a call of (load), so it is necessary to capture the pathname once when lisp initializes.

You could just change the last line to something like:

(if (not (load name))
        (format t "~a not found" name))))

To support Linux you should also look in ~/.audacity-files/plug-ins

Did you verify that this works for you in Linux? As for the ~, that is the environment variable $HOME, correct? If XLisp gives you getenv, then doing the rest in Lisp should be easy enough.

On 'Nix systems, “~/” (tilde, forward-slash) refers to the user home directory.
This is usually something like:
/home//
The first “/” is the root directory. There is nothing below the root directory, that is the start of the file system.
This is the environment variable $HOME (upper case).
In Bash shell:

$ echo $HOME
/home/john-doe

in Nyquist:

(get-env "HOME") 
; returns /home/john-doe

It would probably be best for the “include” function to return “nil” on fail, then for Linux the user code can have something like:

(if (not (include "plug-ins" "hello.lsp"))
    (load (format nil "~a/.audacity-files/plug-ins/hello.lsp"
                  (get-env "HOME"))))

or something like that could be in the “include” function.

Also, Nyquist has a global: file-separator which returns the file separator character “” if on Windows or “/” if on 'Nix.

Perhaps we could use RUNTIME-PATH
On Linux, this points to the nyquist folder where all the .lsp files are.

(print *RUNTIME-PATH*)
; "/usr/local/share/audacity/nyquist/"

Sadly that does not appear to work on Windows.

How about something like this in init.lsp

(setf *NYQPATH* (current-path))

(setf *PLUGINPATH*
  (let ((parentpath (subseq *NYQPATH* 0 (- (length *NYQPATH*) 8))))
    (format nil "~a~a~a" parentpath "Plug-ins" *file-separator*)))

(load (format nil "~a~a" *PLUGINPATH* "audacity.lsp"))

That gives us a lisp file that can ship Audacity specific functions and is in a known relative location.

Perhaps also a plug-ins configuration file?

I wouldn’t put the magic number 8 in the code, which assumes the seven letter folder name “nyquist”. But anyway I think the point is proved that we have the means to fix the problem all in lisp. We would need to diverge init.lsp from Roger’s project but that is all.


I chose to call the function include by an analogy with the #include directive of C. As with C, we could easily define not one but a sequence of path names to be searched for a relatively specified file.

Though the folder name IS “nyquist”. True that it will break if the name is changed, but counting back to the previous file separator will break if the folder hierarchy is changed. Probably better would be to use a symbol for the name of the “nyquist” folder so that if updated it would only need updating in one place, but as you say, at this stage it is just about proof of concept.

I would not expect much objection to that as that is the “proper” place for customization.

Yes, good idea.
What I was thinking was, to keep changes to init.lsp to a minimum and for extensibility, to add an “audacity.lsp” file in which “include” and possibly other useful Audacity specific functions could be added. It’s a little less direct, but I think would make adding more functions (or macros) cleaner because they could be added into the Audacity specific (audacity.lsp) file.