Loop Measure Labels

Hola hombres…

Just wrote some “quick and dirty” code for creating labels for the ends (beginnings?) of measures in loops…

Why the code?
Well, as many of you know, sometimes you “get” loops that are two (or more) measures long but are the
same sample(s) repeated twice. This can be a waste of storage space, so the obvious thing to do is to
chop that loop in half if possible.

I usually just use this technique for beat loops (such as bass drum, hi-hats, etc).

The following code supposes that:

  1. You know how many measures are in the loop.
    (This usually requires a BPM chart, see below…)
  2. Your loop hasn’t been chopped and is accurate length for looping.
    (i.e., it’s in the form that a REPEAT of it would recreate a track).

Here’s the code…

;nyquist plug-in
;version 1
;type generate
;name "M3"
;action "measuring..."
; =========================================
;info "44.1 kHz AuDiO OnLy"
; ======================================================
;control MEZ "#ofM" int "#" 1 1 50
; ======================================================
           (SETQ AUL (/ LEN 44100.000000))
; ======================================================
           (SETQ MZL (/ AUL MEZ))
           (SETQ NMT 0)
           (SETQ NM MEZ)
           (SETQ LOM NIL)
; ======================================================
           (SETQ T01 (CONS (LIST CMT "MeZ") LOM))
           (SETQ LOM T01)
           (SETQ RNM (- NM 1)) : (SETQ NM RNM)
           (IF (> NM 0) (GO LINE-002))
; ======================================================
           (PRINT "=====================")
           (PRINT "Audio Length")
           (PRINT "=====================")
           (PRINT AUL)
           (PRINT "=====================")
           (PRINT "Measure LengthZ")
           (PRINT "=====================")
           (PRINT MZL)
           (PRINT (* MZL 2))
           (PRINT (* MZL 3))
           (PRINT (* MZL 4))
           (PRINT "=====================")
           (PRINT "=====================")
           (PRINT "=====================")
           (PRINT LOM)
; ======================================================

As you can see, it’s not like my usual code (unpretty), but I’ve been really down about coding after some
recent “setbacks” trying to make GUI programs in Rapid-Q Basic, trying to read files in Qbasic, AND
figuring out that microphones “warp” your voice so you sound crappy unless you spend and arm and a leg
on one. As my once hero Anton Newcomb would have said in the 90s, “Take it from the man!” :frowning:
Oh well, I’m more into Cancer Bats these days anyway… :imp:

In addition, the last part of the code (only available by pressing the DEBUG button at time of running)
doesn’t really work as (for some reason) the numbers get rounded to six decimal places. Works
“internally” though. ha ha.

Oh yeah, as for that BPM chart, you need something like this:
As you can see, such a chart is mega-helpfull.

Each measure is given two spaces for the shortest and longest length for that BPM for that particular number
of measures.

The RED cells are data that has been taken from actual loops.
The WHITE cells with itallic numbers are measurements calculated from RED cells.
I put a new sheet for each BPM, and then fill it in as I go.
As I said, mega-helpful for modern music and for using the above code…

Via Cons Dios XLISP coders!

Hi loopman!

Because I also often found it unfortunate that Nyquist prints only the first six digits after the dot I wrote a function that computes the missing digits from the number of samples after the dot, and therefore works best if the floating-point number represents the length of an Audacity selection or the length of a sound. The division part imitates the “pen-and-paper” division method I learned as a child at school. The function is not really fast, do not use it to compute millions of samples, but for printing a few dozend numbers it’s fast enough.

(defun print-seconds (float-time &optional (number-of-digits 15))
  (let* ((seconds (truncate float-time))
         ;; compute the number of samples after the dot
         (samples (round (* (- float-time seconds) *sound-srate*)))
         (sample-rate (round *sound-srate*)))
    (princ seconds)
    (princ #.)
    ;; divide the number of samples after the dot by the sample rate
    (dotimes (n number-of-digits)
      (setf samples (* samples 10))
      (let ((digit (/ samples sample-rate)))  ; integer division!
        (princ digit)
        (setf samples (- samples (* digit sample-rate)))))

The TERPRI function at the end adds a line-break after the number. Just leave the TERPRI function away if you do not want that.

To see how it works just copy the PRINT-SECONDS function from above into the Audacity “Nyquist Prompt” window, add the code below, and press the “Debug” button instead of OK.

(print (get-duration 1.0))
(print-seconds (get-duration 1.0) 30)

The length of the Audacity selection in seconds will be printed twice in the “Debug” window, the first time with 6 digits after the dot, the second time with 30 digits after the dot. Nyquist uses IEEE “double precision” floats, which can represent approx.15 to 17 digits after the dot with reasonable precision, everything after that is mainly rounding noise (no reliable digits anymore). In the case of a sample-rate of only 41000Hz the number of releable digits is already reached at 1/41000, what equals to approx. 5 digits, that even the build-in 6 digits can display correctly.

I will try to work out a more general way to increase the number of printed digits.

  • edgar

While interesting as sample code, wouldn’t it be easier to just use this?

(setq *float-format* "%1.30f")
(print (get-duration 1.0))

which also works on Audacity 1.2.6

To enable Edgar’s code on Audacity 1.2.6, the function round needs to be defined:

(defun round (x) 
  (cond ((> x 0) (truncate (+ x 0.5)))
        ((= (- x 0.5) (truncate (- x 0.5))) (truncate x))
        (t (truncate (- x 0.5)))))

Some time later - here is what is possible using Nyquist integers to print floating-point numbers.

On a 32-bit machine the Nyquist integer limit is:

(1- (round (expt 2.0 31))) => 2147483647

We must take special care to never pass this limit because many XLISP math functions produce wrong results with “wrapped” integers.

Examples on a 32-bit machine:

(round (expt 2.0 31))        => -2147483648  ; wrapped integer
(< 0 (round (expt 2.0 31))   => T            ; wrong!
(> 0 (round (expt 2.0 31))   => NIL          ; wrong!

For the sake of simplicity (= speed) we assume that we only want to print floating-point numbers in decimal format, so a reasonable divisor must a power of ten. We limit our function to a maximum internal integer of:

(1- (round (expt 2.0 31)))  => 2147483647  ; 32-bit integer limit
(round (expt 10.0 9))       => 1000000000  ; maximum internal integer

This means that with Nyquist integers we can display a maximum number of eight digits after the dot on a 32-bit machine. This is not really much better than the built-in PRINT function. However, here is a PRINT-FLOAT function printing eight decimal digits after the dot:

(defun print-float (float)
  (let* ((integer-part (truncate float))
         (digits-after-dot (round (* (- float integer-part) 100000000))))
    (princ integer-part)
    (princ #.)
    ;; compute the digits after the dot
    (dotimes (n 8)
      (setf digits-after-dot (* digits-after-dot 10))
      (let ((digit (/ digits-after-dot 100000000)))  ; integer division!
        (princ digit)
        (setf digits-after-dot (- digits-after-dot (* digit 100000000)))))

I had also looked at the Audacity/Nyquist C-code (audacity/lib-src/ibnyquist/nyquist/xlisp/xlprin.c), but there is no way to just simply increase the number of printed digits, because the string from XLISP’s float-format already is the control string of the underlying “sprintf” C-function and there are no other print options provided by the standard C library.

Hope this helps at least a little bit …

  • edgar

YES!!! Of course, but where is this documented? I searched for nearly an hour but found nothing. I remember we had a discussion about integer and float format strings but can’t find where it was. (If you tell me it’s in my own docs I’ll bite my ass…)

  • edgar

Your poor ass :smiley:

Basic documentation of float-format

Specific examples:

Woah, you dudes are serious about that format stuff.

To me, I think a language should just store whatever you put into it. I never liked that “declare your variable stuff”.
What if the program doesn’t use a variable that was declared? That’s a waste of space!

That’s one of the cool things about XLISP, braughs. You don’t have to use those stupid “$” for string variables (which
I think look ugly). You just pick a variable name, put contents in it, and XLISP does the rest.

Some might say it’s TOO automated a process, but I say that it should go further…
Why HAVE to format or choose a variable type?
Why not “declare” you variable type by your function?
For instance, I say: VAR=1/44100.000000
That means I want six decimal spaces back, even if they are zeros.
That’s the way I’d handle it.

On a bad note, looks like my code is WAY less than useful.
The labels produced WILL be correct (i.e. at the right time of the measure break(s)).
The problem is that when you cut the audio at that point, it will often ROUND the audio to the
nearest sample. Damn you sample!
Thus, when you copy paste 1 measure, it will often NOT be the same length as the original cut loop.

As an example:
4 measure loop at 93 bpm = 10.322585
Label at: 2.580646
When cut to one measure, and “loop/repeated” to 4 measures, new length = 10.322630

It’s not a TON off, but certainly not where I’d want it to be. Oh well.

As a neat tip that I recently learned, you can often use loops that have a 2:1 ratio together and
they will sound “in-time”. For example 80bpm drum loop and 160 bpm perc loop will be “in-time”.
That’s pretty neat.

Often, hip-hop producers compose at higher bpm, but use less notes per measure, thus producing
a track that is slow then goes INSANELY fast for a few seconds. The rise of the 3D audio experience?

A similar issue occurs with Click Track.
See here for how the issue has been resolved: http://bugzilla.audacityteam.org/show_bug.cgi?id=449
(this code is not yet in the release version of Audacity).

The Specific examples link posted by Steve above is not working. Can someone please help me with the right link? Thanks.

Try this alternative link to the same post: Additional *float-format* options - #4 by edgar-rft