EXTRACT and AT don't interact as expected

I’m stumped about the interaction of (extract …) and (at …) and (snd-extent …).

First, here’s a convenience function that just calls snd-extent:

>> (defun x (b) (snd-extent b *autonorm-max-samples*))
=> X

Let’s test it:

>> (x (osc 66))
=> (0 1)

As expected, (osc 66) has start time of 0 and and end time of 1.

>> (x (at 10.0 (osc 66)))
=> (10 11)

Also as expected, (at 10 …) shifts the start and end times by 10. That’s good.

>> (x (extract 0.1 1.1 (osc 66)))
=> (0 0.9)

Still good: calling (extract 0.1 …) on something that has an extent of (0 1) returns (0 0.9) (i.e. the last 0.9 seconds of the sound).

>> (x (extract 10.1 11.1 (at 10.0 (osc 66))))
=> (0 0.0129252)

But now I’m baffled. I would expect this also to return (0.0 0.9), since I’ve shifted the sound to start at 10.0. And I have no idea where the value 0.0129252 comes from.

Any hints?

And as usual, I should show my environment:

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

My theory is that warp is so called because of its affect on the brain. :confused:

I don’t think that this makes sense, hence a nonsensical return value.

>> (x (extract 10.1 11.1 (at 10.0 (osc 66))))
=> (0 0.0129252)

I think that you will find that this gives the same nonsense return value:

(x (extract 10.1 11.1 (osc 66)))

EXTRACT returns the portion of beh relative to the current warp.
AT is shifting warp by time but does not change the local start and stop times of beh. It is just shifting the reference point for local time.

The local start time for behaviour (osc 66) is 0.
The local end time for (osc 66) is 1.
The local start time for behaviour (osc 66 5) is 0.
The local end time for (osc 66 5) is 5.

“(AT 10.0” shifts *warp" by 10.0, but the start and end of beh are relative to warp so when we extract, the AT function has no effect.

;; SND-EXTENT returns the logical start end end times of beh
(x (osc 66 5)) ; => (0 5)
(x (extract 0 1 (osc 66 5))) ; => (0 1)
(x (extract 0 1 (at 100 (osc 66 5)))) ; => (0 1) AT has no effect here. We are still extracting from beh, even though beh is shifted in time.
(x (extract 0.5 1 (osc 66 5))) ; => (0 0.5)
(x (extract 1 2 (osc 66 5))) ; => (0 1)
(x (extract 1 2 (at 123.4 (osc 66 5)))) ; => (0 1) AT has no effect here
(x (extract 4 5 (osc 66 5))) ; => (0 1)
(x (extract 4 10 (osc 66 5))) ; => (0 1) the last 1 second of beh
(x (extract 5 6 (osc 66 5))) ; => (0 0) the end time of beh is at 5.0
(x (extract 6 7 (osc 66 5))) ; => nonsense. Beyond the end of beh

I don’t think that the last example will work without a “anchor” point, ie, a dummy sound at time 0.
Something like (sim (s-rest 0) (at 10.0 (osc c5)))
All functions you’re using are relative, this may cause the odd number (although the stand-alone Nyquist starts with a duration of 1, in contrast to the Audacity Nyquist Prompt, where the selection supplies the duration).
Try the same with the absolute versions (extract-abs, at-abs) or even embed it all in ‘abs-env’. Those functions are mainly based on snd-xform - which is under certain circumstances buggy. There’s a thread about that in this forum.

Also applies to all analyze and process type plug-ins, but not generate type plug-ins. Generate type plug-ins behave the same as stand alone Nyquist in this respect in that the default duration is 1 second.

Yes, that changes the entire context because the start time is now at zero, outside of the warp shift that is being applied to (osc c5).

(x (extract 10.1 11.1 (at 10.0 (osc 66)))) ; => (0 0.0129252) nonsense.

(x (extract 10.1 11.1 
            (sim (s-rest 0)
                 (at 10.0 (osc 66))))) ; => (0 0.9) beh starts at 0.0 and ends at 11.0.

EXTRACT returns the portion of beh relative to the current warp.

I think I’m getting hung up on the definition of “current” (or in more computer science-y terms, the binding scope).

When I do

(at 10 (osc 66))

I assumed the contract of (AT n beh) is to temporarily bind warp to a shifted value of warp while evaluating beh. So in this case, “the current value of warp” is shifted by 10 when it evaluates (osc 66).

But clearly my assumption is wrong.

So I guess my real questions are:

  • what’s the right way to create a behavior (e.g. osc) that starts at time 10 and ends at time 11?
  • If I have a behavior that starts at 10 and ends at 11, what’s the snd-extent of (extract 10.1 11.1 beh)?

Perhaps as a musician rather than a computer scientist I am blissful in my ignorance :smiley:
You may want to take that into account when considering my responses to these complicated issues.


(at 10 (beh duration 1))

That depends on warp.
If warp has been shifted as in this example, then SND-EXTENT of (EXTRACT 10.1 11.1 beh) will be undefined.
If the start time has been “anchored” at time = 0 (as Robert suggested) then SND-EXTENT will be 0.9.

Perhaps an analogy will help:
Let’s say that our “behaviour” is a radio program that lasts for 1 hour and starts at midnight. Midnight is our “time = 0”.
If we write that in pseudo LISP code we might have something like:

(radio 1.0) ; duration of the 'radio' behaviour is 1.0 hours

If we “extract” from that radio program the part between 6 minutes (0.1 hours) and 66 minutes (1.1 hours), then our pseudo code will be:

(extract 0.1 1.1 (radio 1.0)) : returns the final 54 minutes of the radio show

Now let’s say that due to a change in program schedule, the radio show starts at 10 am rather than at midnight.
If we extract that part of the show that lies between 6 minutes and 66 minutes, we still get the final 54 minutes of the show.

(extract 0.1 1.1 (at 10.0 (radio 1.0))) : returns the final 54 minutes of the radio show

How do we extract the final 6 minutes of the show?

(extract 0.9 1.0 (at 10.0 (radio 1.0))) : returns the final 6 minutes of the radio show

The “To” time can be anything >= 1.0 to catch the last 6 minutes, so this will also catch the last 6 minutes of the show:

(extract 0.9 11.1 (at 10.0 (radio 1.0))) : returns the final 6 minutes of the radio show

Clearly if we try to extract from a time value greater than 1.0 we will miss the show because the show was only one hour long.

The point that Robert was making is that we can set a reference point at “midnight” and then when we extract we will not be extracting relative to the start of the show, but relative to midnight.
Let’s imagine that the radio show starts at 10 am, but we start recording at midnight. We have now added a behaviour that starts at time=0, which is that we record whatever is on the radio at that time.

(sim
  (at 0 (other-show))
  (at 10 (radio 1,0)))

Now it DOES make sense to extract from 10.1 to 11.1 and we will end up with the final 54 minutes of the show that we want.

(extract 10.1 11.1
  (sim
    (at 0 (other-show))
    (at 10 (radio 1.0))))

Okay, I’ll surrender, but only after one more comment.

If I have a behavior that starts at 10 and ends at 11, what’s the snd-extent of (extract 10.1 11.1 beh)?

That depends on warp.
If warp has been shifted as in this example, then SND-EXTENT of (EXTRACT 10.1 11.1 beh) will be undefined.

I will show that warp has NOT been shifted:

> (setq a (at 10 (osc 66)))
#<Sound: #4f28b8>
> (snd-extent a 1000000)
(10 11)    ;; <= starts at 10, ends at 11
> *warp*
(0 1 NIL)  ;; <= not shifted

So according to your statement (taken out of context), (snd-extent (extract 10.1 11.1 A) 1000000) should be defined (since warp has not been shifted). But if I read your next statement:

If the start time has been “anchored” at time = 0 (as Robert suggested) then SND-EXTENT will be 0.9.

then I’ll resign myself to anchoring all my ATs at 0, even if I don’t understand why that’s necessary.

Now I’ll surrender. Thanks!

I’m not sure if it helps or not, but the AT transformation is described in the manual here: http://www.cs.cmu.edu/~rbd/doc/nyquist/part4.html#index141

Yep, I’ve re-read that section a few times! :slight_smile: It’s clear that anchoring a (sim …) at time 0.0 makes all the difference, but I didn’t find that in the docs. Thanks!

Look at it in this way:
Sounds are evaluated sequentially. There’s no random access possible due to the implementation.
Let’s say we want to look at the last 10 % of a sound. We can easily display the samples of an oscillator by typing

(print (snd-samples (extract 0.9 1 (osc c5 10)) ny:all))

This shows us the last 44100 samples (if the sample rate is 44.1 kHz).
The evaluation starts in fact at time 0 but the advanceing buffer is simply flushed until the left extraction point is reached. From now on, the values are gathered in an array and sent to the display. It is essentially the same as reading the data from a file. There, you also will discard all bytes until you’ll reach the desired position.
Certain functions force an evaluation (eg, snd-extent, snd-length).
To make this function, all samples must be readable.
In your example above, there is no behaviour given for time 0 until time 10, thus, only the osc tone itself is evaluated and this stops before 10 is reached.
That’s why we need a dummy behaviour at time 0. It is enough to assign it just before any kind of evaluation takes place (writing to disk, playing, displaying etc.).
All intermediate calculations can be done with different start times and so on.
The special environment handling and the different evaluation routine used in e.g. ‘seq’ can make the things tricky sometimes.
I often struggle myself with those issues.