strange behavior of seq

I took a sound of 2 min and made a fade in over 2 min for a better visual recognition.

Then i applied some code to test the seq command. (Please comment in or out the different seq lines)

(setf s090 (multichan-expand #'extract-abs 0 90 s))

(setf s90pl (multichan-expand #'extract-abs 90 (get-duration 1) s))
(setf s05 (multichan-expand #'extract-abs 0 5 s))

;    (seq my-unmodified-snd (at-abs 90 my-selection) s1)

(seq s090)
(seq s05)
(seq s90pl)
(seq s090 s05)
(seq s05 s090)
(seq s090 s05)
(seq s05  s90pl)
(seq  s90pl s05)

what seems to work as I expected are the first 3 , (seq s05 s090) and (seq s05 s90pl).
The rest and perhaps some more ended up with some parts of the result as long as to the end of the original sound.

I can’t understand that.
What’s wrong with that?

This is one of the hardest to understand or explain aspects of Nyquist programming and is complicated even further by how Nyquist runs in Audacity.

Basically the problem is that you are mixing up sounds and behaviours and creating contradictory time maps.
There is a section in the manual about Sounds vs Behaviours: http://www.cs.cmu.edu/~rbd/doc/nyquist/part4.html#28
You may need to read this a hundred times or more (I know that I have :wink:)

Let’s look at how SEQ works (correctly) in a “generate” type plug-in:

(setf s1 (mult 0.5 (noise 2)))
(setf s2 (mult 0.3 (osc 72 5)))

(seq s1 s2)

NOISE and OSC are behaviours.
As expected, this creates 2 seconds of noise followed by 5 seconds of sine tone.


Now let’s see what happens in a “process” type plug-in (an “Effect”).
Using the same code, we get a noise that is 2 times longer than the selection, followed by a sine tone that is 5 times longer than the original selection.
The reason that this happens is because in analyze and process type plug-ins, the selection always has a “logical time” of 1.
This is often useful as many Nyquist functions have a default duration of 1, and we can easily apply these functions to the Audacity selection without needing to be concerned about the duration. For example, in a process type plug-in, (osc 60) will produce a tone of MIDI note 60 with a duration equal to the selection length.


If we want to use “absolute time” in a process type plug-in, we can do so by wrapping the function within (abs-env beh)
For example:

(abs-env
  (progn
    (setf s1 (mult 0.5 (noise 2)))
    (setf s2 (mult 0.3 (osc 72 5)))
    (seq s1 s2)))

Problems with extract-abs:
As described in the manual, even though “S” is a sound rather than a behaviour we can often get round the problem by “cuing” the sound (cue s).
However, this does not get round all of the problems that we might encounter.

If we have a 10 second audio track then we might expect this to work:

(seq
  (extract-abs 9 10 (cue s))
  (extract-abs 0 1 (cue s)))

However it fails “error: bad argument type - NIL”
The problem here is that we set the start time of “S” to 9 seconds. but then we are trying to access from 0 to 1 seconds, which we have just defined as 9 seconds before the start of the sound. In effect we are trying to access something that hasn’t happened :open_mouth:

I’ve changed your last code to

(setf s1 s)
(setf s2 s)
(seq
  (extract-abs 9 10 (cue s1))
  (extract-abs 0 1 (cue s2)))

Can you explain me, why the s2 part of the result is 9 seconds in the result?

Are you sure s2 is 9 seconds?
What was the original length?
s2 should still be the length of the original audio.

You have set a start and stop time for s1 and s2, (but note that these are really just pointing to the same sound as “s” points to).
Nyquist is unable to work out where you want the sequence to end because the stop time for s is 10 seconds (or however long the selection is).

There are two ways round this:

  1. You can set a stop time for the sequence:
(setf s1 s)
(setf s2 s)

(setf output (seq
  (extract-abs 9 10 (cue s1))
  (extract-abs 0 1 (cue s2))))

(extract-abs 0 2 output)
  1. Although Nyquist does not know where you want the sequence to end, it does know the stop time of s2.
    The logical stop time tells Nyquist when the next sound should begin to form a sequence of sounds.
    So what we could do is to add another sound on to the end of the sequence, and that will start at the correct point.
    “but I don’t want another sound at the end…”
    That’s OK, we will add a null sound to the end:
(setf s1 s)
(setf s2 s)
(seq
  (extract-abs 9 10 (cue s1))
  (extract-abs 0 1 (cue s2))
  (s-rest 0))

My multichannel version is getting errors, whatever I try:

(setf s1 s)
(setf s2 s)
(seq
 (multichan-expand #'extract-abs 9 10 (cue s1))
 (multichan-expand #'extract-abs 0 1 (cue s2))
; (multichan-expand #'s-rest 0)
 (s-rest 0)
)

And why Nyqust doesn’t take “The logical stop time” of the last element for the total end time by default?

Sorry but that is probably beyond my ability to explain :smiley:
It’s all to do with warp, the “environment” and the difference between local and global time.

Rather than saying “errors” it is helpful if you say what the errors are.
To avoid big long error logs in you post you can use the “Code” button like this:

error: arguments not compatible
Function: #<Subr-ERROR: #ab966f8>
Arguments:
  "arguments not compatible"
Function: #<FSubr-COND: #ab96ab8>
Arguments:
  ((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 "arguments not compatible"))
Function: #<Closure-FORCE-SRATES: #abc231c>
Arguments:
  #(44100 44100)
  #<Sound: #a95a968>
Function: #<FSubr-PROGV: #ab9c960>
Arguments:
  (QUOTE (*WARP*))
  (IF (WARP-FUNCTION *WARP*) (LIST (LIST (SREF-INVERSE (WARP-FUNCTION *WARP*) T0) (WARP-STRETCH *WARP*) (WARP-FUNCTION *WARP*))) (LIST (LIST T0 (WARP-STRETCH *WARP*) NIL)))
  (FORCE-SRATES S%RATE (S-REST 0))
Function: #<FSubr-PROGV: #ab9c960>
Arguments:
  (QUOTE (*WARP* *SUSTAIN* *START* *LOUD* *TRANSPOSE* *STOP* *CONTROL-SRATE* *SOUND-SRATE*))
  NYQ%ENVIRONMENT
  (AT-ABS T0 (FORCE-SRATES S%RATE (S-REST 0)))
Function: #<Closure: #aa8f4fc>
Arguments:
  0.00553288
1>

The problem is that you are trying to add a mono sound (s-rest 0) with a multi-channel sound.
For it to work, all of the sounds need to have the same number of channels.

If you know in advance that S will always be stereo then you can create a null stereo sound (vector (s-rest 0)(s-rest 0))
If you want to support mono and stereo you need to make it conditional:

(setf s1 s)
(setf s2 s)

;; mono or stereo null sound
(setf null-s
  (if (arrayp s)
      (vector (s-rest 0)(s-rest 0))
      (s-rest 0)))

;;; dummy function so that we can use multichan-expand
(defun dummy (sig)
  sig)

(seq
 (multichan-expand #'extract-abs 9 10 (cue s1))
 (multichan-expand #'extract-abs 0 1 (cue s2))
 (multichan-expand #'dummy null-s))

or possibly neater:

(setf s1 s)
(setf s2 s)

(defun my-sequence (start1 stop1 sig1 start2 stop2 sig2)
  (seq (extract-abs start1 stop1 (cue sig1))
       (extract-abs start2 stop2 (cue sig2))
       (s-rest 0)))

(multichan-expand #'my-sequence 9 10 s1 0 1 s2)