killing nyquist with extract

The following code reliably causes Nyquist to terminate.

My intention is to create a butt splice between x-snd and y-snd after 0.25 seconds. Instead, I seem to be exposing my ignorance of (extract …) or some other fundamental Nyquist concept. Any hints?

;;; butt splice test

(setq x-snd (pluck 52 1.0))  ;; make a pair of sounds
(setq y-snd (pluck 64 1.0))

(play (seq (cue x-snd) (cue y-snd))) ;; works

(setq y-extent (snd-extent y-snd *autonorm-max-samples*)) ;; get duration of y-snd

(setq x-trimmed (extract 0 0.25 (cue x-snd)))  ;; x-snd from 0 to 0.25
(setq y-trimmed (extract 0.25 (cadr y-extent) (cue y-snd))) ;; y-snd from 0.25 to end

(play x-trimmed) ;; works
(play y-trimmed) ;; works

(play (seq x-trimmed y-trimmed))  ;; kills nyquist

Are you using standalone Nyquist or Nyquist in Audacity?
Is your operating system Windows?
If you are running the commands in Audacity, are you running them in the Nyquist Prompt, or within a plug-in?
If you are running them within a plug-in, what type of plug-in (process, generate or analyze)?

Oops – my bad for not describing the environment. I’m running the Nyquist IDE (no Audacity):

  • Mac OS X v 10.8.4 (Mountain Lion)
    NyquistIDE (jNyqIDE) v 2.0
    XLISP version 2.0
    Nyquist Version 3.08

The code I gave is verbatim from a file loaded into Nyquist via the (load ‘filename’) command. And when the file is processed, the last two lines are:

  • Saving sound file to /tmp/r-temp.wav
    The Nyquist process has terminated. Please save your files and exit.

Thanks. I’ll look into this further, but for now, does this work:

(setq x-snd (pluck 52 1.0))  ;; make a pair of sounds
(setq y-snd (pluck 64 1.0))

(setq y-extent (snd-extent y-snd *autonorm-max-samples*)) ;; get duration of y-snd

(setq x-trimmed (extract 0 0.25 (cue x-snd)))  ;; x-snd from 0 to 0.25
(setq y-trimmed (extract 0.25 (cadr y-extent) (cue y-snd))) ;; y-snd from 0.25 to end

(play (sim
  (at 0 (cue x-trimmed))
  (at 0.25 (cue y-trimmed))))

Steve:

I’m glad to know I wasn’t doing anything really incredibly stupid. For now, having the (sim …) workaround is good enough for me, but keep me posted when/if you uncover anything.

2^n thanks.

  • Rob

A little bit of information.
A more simple example of how to trigger the problem:

(setf xsnd (extract 0 0.25 (osc 60)))
(setf ysnd (extract 0.25 1 (osc 72)))
(seq xsnd ysnd)

produces a crash:

nyquist/nyqsrc/sndseq.c:198: sndseq_fetch: Assertion `togo >= 0' failed.
Aborted

The source file sndseq.c contains the following code:

    /* don't run past logical stop time */
    if (!susp->logically_stopped && susp->susp.log_stop_cnt != UNKNOWN) {
        int to_stop = susp->susp.log_stop_cnt - susp->susp.current;
        togo = MIN(togo, to_stop);
    }
    assert(togo >= 0);

I don’t know C programming, so at this point I’ll log it as a bug and hand it over to the developers.

Steve: great sleuth work. Let me know if there’s a way for me to track the bug post.

As the problem is in Nyquist rather than just Audacity’s implementation of Nyquist, I’ve e-mailed Roger Dannenberg (creator of Nyquist) directly. I’ll post updates here if I hear any news about this.

I can see why there is a problem with your original code, but I think that the fact that it causes Nyquist to crash is a bug. “Asserts” are put into code by programmers to flag if something happens that should never happen, as opposed to putting in an “Error” to handle incorrect use.

The Nyquist manual states (Behavioral Abstraction)

stop
Stop time of clipping region. By analogy to start, no sound should be generated after this time. start and stop allow a composer to preview a small section of a work without computing it from beginning to end. You can read stop directly, but use extract or extract-abs to modify it. Note: Due to some internal confusion between the specified starting time and the actual starting time of a signal after clipping, stop is not fully implemented.

EXTRACT does not simply set two parameters for the sound that it is applied to, it sets start and stop which are elements of the global transformation environment.

In your code "stop* is being set to two conflicting values.
In defining x-trimmed you have set stop to 0.25, but you are then trying to run past stop time, which is illegal.

In the revised code the AT transformation handles the conflict properly by shifting the warp component of the environment, thus x-trimmed and y-trimmed are individually computed and then shifted according to AT.

Perhaps SEQ should handle this better, but that’s going a bit too deep for me :wink:

Yep – that all makes sense now. I mistakenly believed that

(setq x-trimmed (extract 0 0.25 (cue x-snd)))

would create a closure that captured the current state of the globals (start, stop, warp, etc) and defer the actual extracting until later. (This is my punishment for knowing too much about lisp.) As I mentioned in https://forum.audacityteam.org/t/when-you-evaluate-a-behavior-is-important/30566/1 I now understand that the extracting takes place at the time that (extract …) is called.

In other words, it all makes sense now. Thanks for helping along the path to enlightenment.

And yes, I also agree that an error message is more appropriate than an assert.

I was looking for an older thread about snd-xform which implements extract and found this. I suspect bugginess in snd-xform, though Steve disagreed with me. Could this be related?

Assertions in programs are supposed to mean “it should be provable that this never happens.” They are not supposed to punish the user for bad inputs. Invocations of Lisp’s (error) from the C code is what is supposed to happen. From what I have read of Nyquist sources so far, they seem conscientious about that difference. Maybe there really is a snd-xform bug leaving things in a bad state? Or can this assertion in seq be got without extract?