Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Non-integer midi notes and pc pitch classes.

192 views
Skip to first unread message

George

unread,
Jan 23, 2023, 11:50:43 PM1/23/23
to Extempore
Ben & Minoru and others
Trying to play a Bali Gamelan type of scale on extempore.
The scale spacings go something like this: '(0 1.97 1.80 3.47 1.04 3.72)
I worked on Ben's suggestion (email to me 14/12/22) on playing non-whole-number midi:

(let ((pitch (midi2frq (random (ivl:melody-by-ivl 61 '(1.97 1.80 3.47 1.04 3.72)))))
(vol 80.0)
(duration 44100.0)
(nargs 0)
(dargs (string->cptr "")))
(xtm_play_note (real->integer (now))
fmsynth
pitch ;; was previously (midi2frq (* 1.0 pitch))
(/ (exp (/ vol 26.222)) 127.0)
(real->integer duration 0) ; bank 0
nargs
dargs))

That went nicely. Ben pointed to a way to write a new "play" macro:
I redefined play-note as "gplay-note" which took a pitch rather than a midi number. And a similar "gplay".
So I could run something like this with the new "gplay":

(:> gplay1 4 0 (play synth @1 60 dur 0) '(49. 50.970000 52.770000 56.240000 57.280000 61. 62.970000 64.770000 68.240000 69.280000 73.000000))

I was going fine on this until by accident I forgot the "g" and just used "play".
The thing worked just as well!
A surprise for me. play and play-note work with non-integer midi notes!!
You get some nice beating effects ...
(:> test1 3 0 (play synth @1 80 dur) '(#(65.0 65.5 66.1 ) #(45.3 45.8 46.3))) ;; OK
(:> test2 8 0 (play synth @1 80 dur ) '( #(39.1 39.5 39.9 40.3) #(49.2 49.5 49.8 50.1) #(55.3 55.5 55.7) #(69.1 69.3 69.5 69.7) #(79.1 79.3 79.5 79.7))) ;;OK

(define play-seq
(lambda (time plst)
(play-note time synth (car plst) 80 11025)
(if (null? (cdr plst))
(callback (+ time 10000) 'play-seq (+ time 11025) '(60.6 62.5 65.7))
(callback (+ time 10000) 'play-seq (+ time 11025) (cdr plst)))))

(play-seq (now) '((60 62 65)))
(play-seq (now) '((60.3 62.5 65.7)))
(play-seq (now) '((60.7 62.1 65.3)))
(play-seq (now) '((60.4 62.6 65.5)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
OK. So play-note and play both work fine with reals for midi numbers!
That's something I didn't know.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

What doesn't work is stuff from "libs/core/pc_ivl.xtm" .
Everything there is built on integers.

To allow a "pitch" to process reals, I modified a couple of lines where "pitch" is converted to integer.
https://github.com/digego/extempore/blob/a3a2563697496c0355c18e4edc92b0c85fe06578/libs/core/pc_ivl.xtm#L288 and 306.
For example:
(let loop ((offset 0)
;;(pitch (real->integer ( pitch-in))) ;; change to:
(pitch pitch-in))

I found that "pc:quantize" and "pc:quantize-low" and other functions don't work with pc lists containing reals like this:
'(0 1.970000 3.770000 7.240000 8.280000)
The culprit turns out to be the built-in "member" function in the definition of pc:? At line 275.
(define pc:?
(lambda (pitch pc)
(list? (member (modulo pitch 12) pc))))
Because "member" can't handle non-integer numbers I made some modifications.
I need a helper to get rid of decimals. Multiply by 100 and round back to integer.
Actually that's a bit of an overkill - I could just use (* 10 x) and have 1 decimal place.
Pitch differences of less than 0.1 are not much audible.

(define (by100 x) (round (* 100 x)))
;; This is my new gamelan version of pc:?
(define pc:?
(lambda (pitch pc)
(list? (member (round (* 100 (modulo pitch 12))) (map by100 pc))))) ;; built-in member

Now lines like this work:
(println (pc:? 3.77 '(0 1.97 3.77 7.24 8.28))) ;; #t
(println (pc:? 67.23 '(0 1.97 3.77 7.24 8.28))) ;; #f
(println (pc:? 67.24 '(0 1.97 3.77 7.24 8.28))) ;; #t
Then for "pc:quantize" and "pc:quantize-low" (291 and 309 ) got rid of (real->integer (round pitch-in)) and just use pitch-in
Also I stepped the offset inc by 0.01 so it can find the second decimal place.

(define pc:quantize
(lambda (pitch-in pc)
(let loop ((offset 0)
;;(pitch (real->integer ( pitch-in)))
(pitch pitch-in))
(cond ((pc:? (+ pitch offset) pc) (+ pitch offset)) ;; offset has to go up in 0.01 steps
((pc:? (- pitch offset) pc) (- pitch offset))
((< offset 7) (loop (+ offset 0.01) pitch))
(else (log-info "no pc value to quantize to" pitch pc)
#f)))))
Now all that works well.
(println (pc:quantize 61.80 '(0 1.97 3.77 7.24 8.28))) ;; 61.970000
(println (pc:quantize-list '(60.3 64 67.4 49.2 69) '(0 1.97 3.77 7.24 8.28))) ;; (60.00 63.77 67.24 49.97 68.28)
(println (ivl:invert '(60 64 67 49 69) 67)) ;; (60 70 67 85 65)
(println (ivl:invert '(60 64 67 49 69) 67.5)) ;; (60 71.00 68.00 86.00 66.00)
I did simple tests on most of the functions defined in "pc_ivl.xtm" and found that many work just as well in my modified version.
What is not working at this stage is: "pc:random", "pc:make-chord" and "pc:degree" "pc:relative". Also "scale" "scale type" and others relating to diatonic.
Maybe an exhaustive test might find some more problems. Anywhere there is an "=" test or "member" or "real->integer" or "filter" could be a problem.

"pc:relative" just doesn't see the reals. It counts only the integers when it is counting steps.
(pc:relative 60 3 '(0 2 4.5 5.5 7 9 11)) ;; 69 It doesn't "see" the reals.
For each of the 3 steps in this example, the code would have to test in 0.1 steps to be able to find the correct real number in the pc list. Haven't got that to work yet.

A big problem I did find is with "pc:random" at line 326.
Now "pc:random" uses the inbuilt Scheme "filter" function which is unable to handle lists of real numbers.
I tried several versions using the same technique on "filter" already used successfully to modify "pc:?" but I couldn't to get it to work.
Am interested in any suggestions on how to progress here.
I hope one of you people with better programming skills than mine can find a way to fix these problems.
I think if "pc:random" and some others are fixed, we will have a version of extempore capable of playing non-integer midi notes.
The diatonic stuff mightn't be so important for these kinds of tunings anyhow so it wouldn't matter that some pc features don't work.
This may go some way towards Ben's intriguing footnote suggestion that one could "build a set of post-colonial extensions".
Regards George

minoru yamaguchi

unread,
Jan 24, 2023, 1:11:19 AM1/24/23
to extemp...@googlegroups.com
Hi, George ..

Ya, because OSCs, instruments and samplers of extmpore use
"frequency" in their code, of course. The sound can't be processed
without "frequency".

I think it is useful to play pitch bending, "choking", like Jimi Hendrix...

2023年1月24日(火) 13:50 George <geor...@bigpond.net.au>:
> --
> You received this message because you are subscribed to the Google Groups "Extempore" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to extemporelan...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/extemporelang/9e386ada-2b6b-4a92-8797-fae8bede0798n%40googlegroups.com.



--
Minoru Yamaguchi

minoru yamaguchi

unread,
Jan 24, 2023, 1:25:45 AM1/24/23
to extemp...@googlegroups.com
Hi George,

How is it to add some real numbers after getting return values by
pc:any-funcs....
Because it's not easy to change many pc:any-funcs .... but making a or
some functions that add some real numbers is not so difficult, I guess
so .....

2023年1月24日(火) 15:10 minoru yamaguchi <mils...@gmail.com>:
--
Minoru Yamaguchi

Minoru

unread,
Jan 24, 2023, 7:16:46 AM1/24/23
to Extempore
;; Hi, George,
;;
;; I made these for now, as quick answer.

(define get-r-pc
    (lambda (pc)
       (cond ((null? pc) '())
                  ((integer? (car pc)) (get-r-pc (cdr pc)))
                  (else
                     (cons (car pc) (get-r-pc (cdr pc)))))))

(get-r-pc '(0 2 3.5 4 ))      ; (3.500000)
(get-r-pc '(0 2 3.5 4 6.2)) ;(3.500000 6.200000)
(get-r-pc '(0 2 4))              ;NIL

(define r-range
  (lambda (r-lis rng-lis)
     (cond ((null? rng-lis) '())
                ((= 0 (modulo (car rng-lis) 12))
                  (append (map (lambda (x)(+ x (car rng-lis)))
                                             r-lis)
                                 (r-range r-lis (cdr rng-lis))))
                (else (r-range r-lis (cdr rng-lis))))))

(r-range '(3.5 4.5) (range 60 70))                        ;(63.500000 64.500000)
(r-range '() (range 60 70))                                     ; NIL
(r-range (get-r-pc '(0 2 3.5 4.5)) (range 60 73)) ; (63.500000 64.500000 75.500000 76.500000)
(r-range (get-r-pc '(0 2 4)) (range 60 73))           ;NIL

;; extempore Original one
;; (define pc:random
;;   (lambda (lower upper pc)
;;     (if (null? pc) #f
;;         (let ((choices (filter (lambda (x) (pc:? x pc)) (range lower upper))))
;;           (if (null? choices) #f
;;               (random choices))))))

;; I use original pc:? function.

(define G-pc:random
  (lambda (lower upper pc)
    (if (null? pc) #f
        (let* ((choices (filter (lambda (x) (pc:? x pc)) (range lower upper)))
                  (add-choices (append (r-range (get-r-pc pc) choices)
                                                          choices)))
            (if (null? add-choices) #f
                 (random add-choices))))))

(G-pc:random 36 73 '(0 2.2 3.3 5.5 7)) ;36, 72, 72, 67, 41.5, 51.3, 63.3,,,,,,
(G-pc:random 60 73 '(0 7 2.2))              ;74.2, 74.2, 72, 60, 72, 62.2,,,,,,,


2023年1月24日火曜日 15:25:45 UTC+9 Minoru:

Minoru

unread,
Jan 24, 2023, 9:18:51 PM1/24/23
to Extempore
;; Hi, George

;; original one is pc:make-chord
;;   then 2 lines fixed by minoru
;;   and now some lines are modified by minoru
;;   given new name, G-pc:make-chord
;;
;; this uses G-pc:random, get-r-pc, r-range

(define G-pc:make-chord
  (lambda (lower upper number pc)
    (let ((chord '()))
      (let loop ((l (round lower))
                       (u (round upper))
                       (n number)
                       (p pc))
        (if (< n 1)
            (map (lambda (x)
                   ;; changed this line to below by minoru
                   ;; (real->integer x))
                   x)
                 ;; changed this to below by minoru
                 ;; (cl:sort (cl:remove -1 chord) <)) ; lowest pitch to highest pitch remove -1s
                 (cl:sort chord <)) ; lowest pitch to highest pitch remove 'nothing'
            (let* ((range (- u l))
                       (gap (round (/ range n)))
                     ;; changed this line to below by minoru ;
                     ;; (pitch (pc:random l (+ l gap) p)))
                      (pitch (G-pc:random l (+ l gap) p)))
                (if (not pitch) ; if new pitch is #f, try from whole range
                    ;; changed this to below by minooru
                    ;; (set! chord (cons (pc:random lower upper p) chord))
                    (set! chord (cl:remove #f (cons (G-pc:random lower upper p) chord)))
                    (set! chord (cons pitch chord)))
              (loop (+ l gap)
                        u
                        (- n 1)
                       ;; delete these 3 lines
                       ;; (if (> (length p) 1)
                       ;;     (cl:remove (modulo (car chord) 12) p)
                       ;;     pc))))))))
                       pc)))))))

(G-pc:make-chord 60 80 3 '(0 4 7))
;;(60.000000 67.000000 76.000000),

(G-pc:make-chord 60 80 3 '(0 2.2 4 7 2.5))
;;(60.000000 67.000000 79.000000), (62.200000 72.000000 76.000000),(62.200000 74.500000 79.000000), etc
(G-pc:make-chord 60 80 3 '(0 2.2 4 7 3.3))
;; (60.000000 74.200000 76.000000),(62.200000 67.000000 76.000000), etc

(G-pc:make-chord 60 80 4 '(0 2.2 4 7))
(G-pc:make-chord 60 80 7 '(0 2.2 4 7))
;; (60.000000 64.000000 67.000000 72.000000 74.200000 76.000000 79.000000), etc

(for-each (lambda (x) (play-note (now) fmsynth x 60 (* 44000 1)))
          (G-pc:make-chord 60 80 4 '(0 4 7)))

(for-each (lambda (x) (play-note (now) fmsynth x 60 (* 44000 1)))
          (G-pc:make-chord 60 80 3 '(0 3.3 4 4.4 7)))
(G-pc:make-chord 60 80 3 '(0 3.3 4 4.4 7))

(for-each (lambda (x) (play-note (now) fmsynth x 60 (* 44000 1)))
          (G-pc:make-chord 60 80 6 '(0 3.3 4 4.4 7)))
(G-pc:make-chord 60 80 6 '(0 3.3 4 4.4 7))

(for-each (lambda (x) (play-note (now) fmsynth x 60 (* 44000 1)))
          (G-pc:make-chord 60 80 5 '(0 2.2 4 7)))
(G-pc:make-chord 60 80 5 '(0 2.2 4 7))
(pc:make-chord 60 80 5 '(0  4 7))

;; and new get-r-pc, more compact one, I like cond version though.
(define get-r-pc
   (lambda (pc)
      (filter (lambda (x) (not (integer? x))) pc)))


(get-r-pc '(0 2 3.5 4 ))    ; (3.500000)
(get-r-pc '(0 2 3.5 4 6.2)) ;(3.500000 6.200000)
(get-r-pc '(0 2 4))         ;NIL

2023年1月24日火曜日 21:16:46 UTC+9 Minoru:

Minoru

unread,
Jan 24, 2023, 9:29:34 PM1/24/23
to Extempore
;; Hi, george
;; Maybe this is useful for you ...

(range 60 64)     ;(60 61 62 63)
(range 60 64 2)  ;(60 62)
(range 60 64 .1) ;(60.000000 60.100000 60.200000 60.300000 60.400000 60.500000 60.600000 60.700000 60.800000 60.900000 61.000000 61.100000 61.200000 61.300000 61.400000 61.500000 61.600000 61.700000 61.800000 61.900000 62.000000 62.100000 62.200000 62.300000 62.400000 62.500000 62.600000 62.700000 62.800000 62.900000 63.000000 63.100000 63.200000 63.300000 63.400000 63.500000 63.600000 63.700000 63.800000 63.900000)
(range 60 64 .01) ;  (60.00 60.01 60.02 ....  400 numbers .... )

2023年1月25日水曜日 11:18:51 UTC+9 Minoru:

George

unread,
Jan 24, 2023, 11:43:03 PM1/24/23
to Extempore
Minoru
You are a fast worker!
I'm still checking out your G-pc:random.
It is going very nicely - thank you.

;; G-pc:random
;; OK that works nicely for both real and integer pcs
;; no matter which pc:? version is loaded - the original or mine.
;; That's good because I think I need my version of pc:? to make pc:quantize variants work.
;; Maybe you have a different idea on that.

;; However I had in mind that there would be just one version of each pc:whatever
;; which would function seamlessly in integer or real pc situations, and not require
;; choosing a different function in different situations.
;; As you say "Because it's not easy to change many pc:any-funcs".
;; But as far as I can see, renaming your G-pc:random to the regular pc:random name,
;; and keeping my version of pc:? causes no problems. I find that it all works fine!

;; pc:random works for integer and real midi numbers:
(dotimes (i 9)
         (print (pc:random 60 85 '(0 2 4 7 9)) " ")  i)

(dotimes (i 9)
         (print (pc:random 48 73 '(0 1.97 3.77 7.24 8.28)) " ")  i)
;; This selects from a possible 15 in the add-choices list:
; 48
; 49.970000
; 51.770000
; 55.240000
; 56.280000
; 60
; 61.970000
; 63.770000
; 67.240000
; 68.280000
; 72
; 73.970000
; 75.770000
; 79.240000
; 80.280000

;; No problems until I get to use your G-pc:make-chord.
;; That works brilliantly as you sent it to me and you can get some lovely
;; interesting sounds just by making up any old pc list of reals and integers.

(for-each (lambda (x) (play-note (now) synth x 60 (* 44000 3)))
          (G-pc:make-chord 60 83 3 '(0 3.3 3.6 4.9 4.4 7)))

;; When I tried reverting the name to pc:make-chord there were protests,
;; so I will have another look at it.

Minoru, thanks so much for your help. We are getting close to what I had in mind.
I'll let you know how I go.

If you have the energy could you have a look at pc:relative for me Minoru.

Regards
George

Minoru

unread,
Jan 25, 2023, 9:12:03 AM1/25/23
to Extempore
;; George,
;;
;; I think that G-pc:relative works well like below.....
;; though my algorithm is very different from original one.

(define G-pc:relative
     (lambda (p n pc)
         (set! pc (rotate pc (* -1 n)))
         (+ p  (car pc)  (if (negative? n)
                                       (* (+ 1 (abs (quotient (+ n 1) (length pc)))) -12)
                                       (* (quotient n (length pc)) 12)))))

;; results .....
(let ((p 60) (n1 15) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;; ((60 64 67 72 76 79 84 88 91 96 100 103 108 112 115)  <-- G-pc:relative
;;  (60 64 67 72 76 79 84 88 91 96 100 103 108 112 115)) <-- pc:relative


(let ((p 60) (n1 15) (pc '(0 2.2 4 7)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;; ((60 62.2 64 67 72 74.2 76 79 84 86.2 88 91 96 98.2 100)  <-- G-pc:relative
;;  (60          64 67 72          76 79 84          88 91 96          100 103 108 112 115)) <-- pc:relative


(let ((p 60) (n1 -10) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (let loop ((i 0))
    (if (< i n1)
        (list (reverse ans1) (reverse ans2))
        (loop (- i 1)
              (set! ans1 (cons (G-pc:relative p i pc) ans1))
              (set! ans2 (cons (pc:relative   p i pc) ans2))))))
;; ((60 55 52 48 43 40 36 31 28 24 19)  <-- G-pc:relative
;;  (60 55 52 48 43 40 36 31 28 24 19)) <-- pc:relative


(let ((p 60) (n1 -10) (pc '(0 4 4.5 7)) (ans1 '()) (ans2 '()))
  (let loop ((i 0))
    (if (< i n1)
        (list (reverse ans1) (reverse ans2))
        (loop (- i 1)
              (set! ans1 (cons (G-pc:relative p i pc) ans1))
              (set! ans2 (cons (pc:relative   p i pc) ans2))))))

;; ((60 55 52.5 52 48 43 40.5 40 36 31 28.5)  <-- G-pc:relative
;;  (60 55          52 48 43          40 36 31          28 24 19)) <-- pc:relative

Regards
2023年1月25日水曜日 13:43:03 UTC+9 George:

Minoru

unread,
Jan 26, 2023, 1:38:01 AM1/26/23
to Extempore
;; Hi ;George,
;;
;; I modified G-pc:relative to do non "12 tone music",
;; though I'm not sure that this explanation is correct or not.
;; But I guess that probably this is one of what you want.
;; You can make your own scale or .....
;;
;; Anyway this G-pc:relative has option variable for degree(??) or pseudo octave(??).
;;   It has the same function as the last time without this option variable.
;;  
;;   Caution : option value should be more than PC
;;             but it's ok if you like results which is some funny ....
;;

(define G-pc:relative
    (lambda (p n pc . opt)
        (let ((wts (if (null? opt) 12 (car opt))))

           (set! pc (rotate pc (* -1 n)))
           (+ p (car pc) (if (negative? n)
                                      (* (+ 1 (abs (quotient (+ n 1) (length pc)))) (* -1 wts))
                                      (* (quotient n (length pc)) wts))))))


;; some results are ....
;; A.
(let ((p 60) (n1 10) (pc '(0 1 2)) (ans1 '()) (ans2 '()))

  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 61 62 72 73 74 84 85 86 96)
;; (60 61 62 72 73 74 84 85 86 96))

;; B.
(let ((p 60) (n1 10) (pc '(0 1 2)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 3) ans1)) ;<-- option

    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 61 62  63 64 65  66 67 68  69)                <-- G:pc:relative
;; (60 61 62                      72 73 74 84 85 86 96))  <-- pc:relative

;; C.
(let ((p 60) (n1 10) (pc '(0 1 2)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 5) ans1)) ;<--- option

    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 61 62   65 66 67   70 71 72  75)
;; (60 61 62                    72 73 74 84 85 86 96))

;; D.
(let ((p 60) (n1 15) (pc '(0 1 1.2 1.5)) (ans1 '()) (ans2 '()))

  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 61 61.2 61.5 72 73 73.2 73.5 84 85 85.2 85.5 96 97 97.2)
;; (60 61           72 73           84 85           96 97 108 109 120 121 132 133 144))

;; E.
(let ((p 60) (n1 15) (pc '(0 1 1.2 1.5)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 4) ans1)) ;<--- option,
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 61 61.2 61.5 64 65 65.2 65.5 68 69 69.2 69.5 72 73 73.2)
;; (60 61                                           72 73 84 85 96 97 108 109 120 121 132 133 144))


;; F.
(let ((p 60) (n1 15) (pc '(0 1 1.2 1.5)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 3) ans1)) ;<--- option > PC

    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 61 61.2 61.5 63 64 64.2 64.5 66 67 67.2 67.5 69 70 70.2)
;; (60 61                                                     72 73 84 85 96 97 108 109 120 121 132 133 144))

;; G.
(let ((p 60) (n1 15) (pc '(0 1 1.2 1.5)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 2) ans1)) ;<--- option > PC

    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 61 61.2 61.5 62 63 63.2 63.5 64 65 65.2 65.5 66 67 67.2)
;; (60 61     72 73     84 85     96 97    108 109    120 121 132 133 144))


;; H.
(let ((p 60) (n1 15) (pc '(0 1 1.2 1.5)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 1.5) ans1)) ;<--- option = PC

    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60.0 61.0 61.2 61.5 61.5 62.5 62.7 63.0 63.0 64.0 64.2 64.5 64.5 65.5 65.7)
;; (60 61        72 73     84 85    96 97    108 109 120 121 132 133 144))

;; I.
(let ((p 60) (n1 15) (pc '(0 1 1.2 1.5)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 1.6) ans1)) ;<--- option > PC

    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60.0 61.0 61.2 61.5 61.6 62.6 62.8 63.1 63.2 64.2 64.4 64.7 64.8 65.8 66.0)
;; (60 61 72 73 84 85 96 97 108 109 120 121 132 133 144))


;; J.

(let ((p 60) (n1 15) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 64 67 72 76 79 84 88 91 96 100 103 108 112 115)
;; (60 64 67 72 76 79 84 88 91 96 100 103 108 112 115))

;; K.

(let ((p 60) (n1 15) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc 5) ans1)) ;<--- not good, option 5 < PC 7

    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;;((60 64 67 65 69 72 70 74 77 75 79 82 80 84 87)

;; (60 64 67 72 76 79 84 88 91 96 100 103 108 112 115))

;; L.

(let ((p 60) (n1 -10) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (let loop ((i 0))
    (if (< i n1)
        (list (reverse ans1) (reverse ans2))
        (loop (- i 1)
              (set! ans1 (cons (G-pc:relative p i pc) ans1))
              (set! ans2 (cons (pc:relative   p i pc) ans2))))))
;; ((60 55 52 48 43 40 36 31 28 24 19)  <-- G-pc:relative
;;  (60 55 52 48 43 40 36 31 28 24 19)) <-- pc:relative

;; M.
(let ((p 60) (n1 -10) (pc '(0 1 2)) (ans1 '()) (ans2 '()))

  (let loop ((i 0))
    (if (< i n1)
        (list (reverse ans1) (reverse ans2))
        (loop (- i 1)
              (set! ans1 (cons (G-pc:relative p i pc 4) ans1)) ;<- ok, option 4 > PC 2

              (set! ans2 (cons (pc:relative   p i pc) ans2))))))
;;((60 58 57 56 54 53 52 50 49 48 46)
;; (60                   50 49 48    38 37 36 26 25 24 14))

;; N.

(let ((p 60) (n1 -10) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (let loop ((i 0))
    (if (< i n1)
        (list (reverse ans1) (reverse ans2))
        (loop (- i 1)
              (set! ans1 (cons (G-pc:relative p i pc 3) ans1)) ; <- not good, option 3 < PC 7

              (set! ans2 (cons (pc:relative   p i pc) ans2))))))
;;((60 64 61 57 61 58 54 58 55 51 55)

;; (60 55 52 48 43 40 36 31 28 24 19))


;; O.

(let ((p 60) (n1 -10) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (let loop ((i 0))
    (if (< i n1)
        (list (reverse ans1) (reverse ans2))
        (loop (- i 1)
              (set! ans1 (cons (G-pc:relative p i pc 4) ans1)) ; <- not good, option 4 < PC 7

              (set! ans2 (cons (pc:relative   p i pc) ans2))))))
;;((60 63 60 56 59 56 52 55 52 48 51)

;; (60 55 52 48 43 40 36 31 28 24 19))

;; P.

(let ((p 60) (n1 -10) (pc '(0 4 7)) (ans1 '()) (ans2 '()))
  (let loop ((i 0))
    (if (< i n1)
        (list (reverse ans1) (reverse ans2))
        (loop (- i 1)
              (set! ans1 (cons (G-pc:relative p i pc 8) ans1)) ;<- ok, option 8 > PC 7

              (set! ans2 (cons (pc:relative   p i pc) ans2))))))
;;((60 59 56 52 51 48 44 43 40 36 35)

;; (60 55    52    48    43 40 36 31 28 24 19))

;; Q.
(let ((p 60) (n1 15) (pc '(0 1 2 3 4 5 6 7 8 9 10 11)) (ans1 '()) (ans2 '()))

  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))
;; ((60 61 62 63 64 65 66 67 68 69 70 71 72 73 74)
;;  (60 61 62 63 64 65 66 67 68 69 70 71 72 73 74))


;; R.
(let ((p 60) (n1 30) (pc '(0 1 1.1 2 2.2 3 3.3 4 4.4 5 6 7 8 9 10 11)) (ans1 '()) (ans2 '()))

  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))

;; ((60 61 61.1 62 62.2 63 63.3 64 64.4 65 66 67 68 69 70 71 72 73 73.1 74 74.2 75 75.3 76 76.4 77 78 79 80 81)
;;  (60 61      62      63      64      65 66 67 68 69 70 71 72 73      74      75      76      77 78 79 80 81 82 83 84 85 86 87 88 89))


;; S.
(let ((p 60) (n1 30) (pc '(0 1 1.1 2 2.2 3 3.3 4 4.4 )) (ans1 '()) (ans2 '()))

  (dotimes (i n1)
    (set! ans1 (cons (G-pc:relative p i pc) ans1))
    (set! ans2 (cons (pc:relative   p i pc) ans2)))
  (list (reverse ans1) (reverse ans2)))

;;((60 61 61.1 62 62.2 63 63.3 64 64.4 72 73 73.1 74 74.2 75 75.3 76 76.4 84 85 85.1 86 86.2 87 87.3 88 88.4 96 97 97.1)
;; (60 61      62      63      64      72 73      74      75      76      84 85      86      87      88      96 97     98 99 100 108 109 110 111 112 120 121 122 123 124))

Regards
2023年1月25日水曜日 23:12:03 UTC+9 Minoru:

Minoru

unread,
Jan 27, 2023, 9:00:33 AM1/27/23
to Extempore
;; George
;;
;; this is modified G-pc:random that has optional argument
;; without option, same as usual pc:random,
;; of course, it's possible to accept real number though,
;; if you use optional argument, it means pseudo octave or
;; optional value tone scale or something, I don't know what I should say it.

;;  this is same as before

(define get-r-pc
    (lambda (pc)
        (filter (lambda (x) (not (integer? x))) pc)))

;; new r-range, added argument
(define r-range
    (lambda (r-lis rng-lis tn)
         (cond ((null? rng-lis) '())
                    ((= 0 (modulo (car rng-lis) tn))

                      (append (map (lambda (x)(+ x (car rng-lis)))
                                                r-lis)
                                      (r-range r-lis (cdr rng-lis) tn)))
                    (else (r-range r-lis (cdr rng-lis) tn)))))

;; modified, having optional argument
;;   without option, it is same as original one.
(define pc:?
    (lambda (pitch pc . opt)
       (list? (member (modulo pitch (if (null? opt) 12 (car opt)))  pc))))

;; modified, having optional argument
(define G-pc:random
    (lambda (lower upper pc . opt)
        (if (null? pc) #f
             (let* ((tn (if (null? opt) 12 (car opt)))
                       (choices (filter (lambda (x) (pc:? x pc tn)) (range lower upper)))
                       (pc1 (get-r-pc pc))
                       (add-choices (if (null? pc1) '() (r-range pc1 choices tn)))
                       (ans (append choices add-choices)))
                (if (null? ans) #f
                     (random (cl:remove-duplicates ans)))))))


;; anyway, these are examples ....
;; do 200 times (G-pc:random ....)

(let ((lower 36) (upper 74) (pc '(0 1.1 2)))
  (let loop ((start 0)(end 200) (ans '()))
     (if (= start end)
          (cl:sort (cl:remove-duplicates ans) <)
          (loop (+ 1 start) end  (cons (G-pc:random lower upper pc) ans)))))
;; results (36 37.1 38 48 49.1 50 60 61.1 62 72 73.1)

;; explanation about above results
;; without option, default value = 12,
;; so, same as (G-pc:random lower upper pc 12)
;;
;;  36= 36+0, 37.1= 36+1.1, 38=36+2
;;  then jump 12, because 12 tone 1 octave
;;  48= 36+12, 49.1= 36+12+1.1, 50=36+12+2,
;;  then jump 12
;;  60= 36+12+12, 61.1=36+12+12+11 ... and so on .....


(let ((lower 36) (upper 74) (pc '(0 1.1 2)))
    (let loop ((start 0)(end 200) (ans '()))
        (if (= start end)
             (cl:sort (cl:remove-duplicates ans) <)
             (loop (+ 1 start) end  (cons (G-pc:random lower upper pc 4) ans))))) ; <- option
;; (36 37.1 38 40 41.1 42 44 45.1 46 48 49.1 50 52 53.1 54 56 57.1 58 60 61.1 62
;;  64 65.1 66 68 69.1 70 72 73.1)

;; explanation about above results
;; 36= 36+0, 37.1= 36+1.1, 38=36+2
;; then jump 4, because 4 tone (option value is 4), pseudo octave
;; of course this pseudo octave frequency is not 2 times
;; 40= 36+4+0, 41.1=36+4+1.1, 42=36+4+2
;; then jump 4, again
;; 44= 36+4+4+0, 45.1=36+4+4+1.1  and so on ...

2023年1月26日木曜日 15:38:01 UTC+9 Minoru:

George

unread,
Jan 27, 2023, 5:27:13 PM1/27/23
to Extempore
Minoru 
It looks like you are on a roll!

I like your idea of pseudo octaves and your modulo not always being 12.

Haven’t tried to make sounds with it yet but looking forward to trying them.


I have run tests on some of the defined functions in pc_ivl.xtm to find what works using real midi numbers as well as integers.

There are quite a few not relevant I think.

Anyhow here’s my list so far:


(define *pc:scales*

(define *pc:chord-syms*

(define *pc:chord-syms-scales*

(define *pc:chord->scale*            ?

(define pc:pc

(define pc:?                             OK

(define pc:quantize                OK

(define pc:quantize-low            OK

(define pc:random                OK

(define pc:relative                OK

(define pc:make-chord            OK

(define pc:degree

(define pc:quantize-list            OK

(define pc:invert                OK

(define pc:transpose                OK

(define pc:expand/contract            OK

(define pc:chord->scale

(define pc:scale

(define pc:diatonic

(define pc:chord

(define pc:chord-options

(define pc:make-chord            OK

(define pc:make-chord-fixed

(define pc:distance-of-chord

(define pc:distance

(define pc:closest-pc

(define pc:closest-pitch

(define pc:find-closest

(define pc:move-chord

(define pc:scale-from-pc

(define pc:from-intervals

(define pc:from-steps

(define pc:scale-from-chord

(define pc:melody-by-step

(define ivl:melody-by-ivl            OK


I’m not sure what is the intention of pc:pc 

What is meant by pitch class?

I think it is the number of integer steps in the scale?

(define pc:pc

  (lambda (pitch)

    (modulo pitch 12)))


(pc:pc 67) -> 7 which I think means 7th note in the C major scale.

Or the 7th note of a keyboard  octave.


But shouldn’t it depend on which scale your are currently in?

Not all scale spaces add up to 11 like ionian ‘(0 2 4 5 7 9 11)

(ionian . (2 2 1 2 2 2)) and many others in *pc:scales*

(pentatonic . (2 2 3 2)) adds to 9.


If you are working a gamelan “scale” this version '(0 1.97 3.77 7.24 8.28) it adds to 8.28

(pc:pc 67.24) -> 7.24  which is not very interesting.

Wouldn’t it be more interesting to  -> 4 which tells you it is the 4th note of this scale?


So the definition could include the pc list and return the position of the pitch in the list, or an error if not in the list.

(define pc:pc

  (lambda (pitch pc)

          ………

Regards

George

Minoru

unread,
Jan 27, 2023, 11:42:11 PM1/27/23
to Extempore
;; Hi, George
;;
;; I make new G-pc:make-chord which has an optional argument,
;; it means non 12 tone ... what should I call it, ..
;; anyway 4 tone pseudo octave(not octave though) ... 4 tone circle ??
;;
;;   new G-pc:make-chord that has optional argument,
;;    

(define G-pc:make-chord
  (lambda (lower upper number pc . opt)
       (let ((tn (if (null? opt) 12 (car opt)))

               (chord '()))
          (let loop ((l (round lower))
                           (u (round upper))
                           (n number)
                           (p pc))
             (if (< n 1)
                  (map (lambda (x) x)
                            (cl:sort chord <)) 
                  (let* ((range (- u l))
                            (gap (round (/ range n)))
                            (pitch (G-pc:random l (+ l gap) p tn)))

                     (if (not pitch) ; if new pitch is #f, try from whole range
                         (set! chord (cl:remove #f (cons (G-pc:random lower upper p tn) chord)))

                         (set! chord (cons pitch chord)))
                     (loop (+ l gap)
                               u
                              (- n 1)
                              pc)))))))

;; examples and results ...
;; 100times G-pc:make-chord

(let ((lower 60) (upper 80) (n 3) (pc '(0 1 2)))
  (let loop ((st 0) (end 100)(ans '()))
    (if (= st end)

        (cl:sort (cl:remove-duplicates ans) <)
        (loop (+ 1 st) end (append (G-pc:make-chord lower upper n pc) ans)))))
;; (60.0 61.0 62.0 72.0 73.0 74.0)
;; 60=60+0, 61=60+1, 62=60+2
;; 72=60+12+0, 73=60+12+1, 74=60+12+2 ...

(let ((lower 60) (upper 80) (n 3) (pc '(0 1 2)))
  (let loop ((st 0) (end 100)(ans '()))
    (if (= st end)

        (cl:sort (cl:remove-duplicates ans) <)
        (loop (+ 1 st) end (append (G-pc:make-chord lower upper n pc 4) ans))))) ;<-- option
;; (60.0 61.0 62.0 64.0 65.0 66.0 68.0 69.0 70.0 72.0 73.0 74.0 76.0 77.0 78.0
;; 60=60+0, 61=60+1, 62=60+2
;; not 12 tone but 4 tone by optional argument
;; 64=60+4+0, 65=60+4+1, 66=60+4+2,
;; 68=60+4+4+0, 69=60+4+4+1 .... and so on ...

;; that'all, ok, this new G-pc:make-chord made results as intended.

;; And I know that above "option 4" or "4 tone circle" or "modulo 4"
;; is not a PC set any more, because 1st 0 is 60(C4) and 2nd 0 is 64(E4),
;; not same sound(tone?) ....
;;
;; On the other hand, "no option" or "modulo 12" or normal G-pc:make-chord
;; brings results like, 1st 0 is 60(C4) and 2nd 0 is 72(C5), that is,
;; 0 is always C, so called PC set or theory or anything.....
;;
;; Anyway, what matters is whether I could make a tune I love ....
;;

Regards !
2023年1月28日土曜日 7:27:13 UTC+9 George:

Minoru

unread,
Jan 28, 2023, 1:02:14 AM1/28/23
to Extempore
George,

Thank you very much for the list you made, which took much time, maybe.
I think that really it is useful for me, too.

2023年1月28日土曜日 7:27:13 UTC+9 George:
Minoru 

George

unread,
Jan 28, 2023, 3:35:16 AM1/28/23
to Extempore
Minoru
I'm beginning to agree with you that these special "G-pc:functions" should have their own names. It seems that most functions you have
built so far work with a mixture of reals and integers so that is a feature worth maintaining for all G-pc:functions.
All these modified functions should behave "normally" when handling only regular midi numbers.
I note that your approach relies on regular "pc:functions"  simply ignoring reals and you have added functions like "get-r-pc" 
that pick out the real numbers and you append these to the final list. My approach with pc:? was to convert the original pc list
to an integer list by multiplying by 100 and rounding. So "member" will inspect that list. I did try the same approach for other pc:functions
but I wasn't skilful enough to get that to work.

Another thing I tried while working on "scales" was to define a new scale:
(set! *pc:scales* (append *pc:scales* '((gamelan-1 . (1.97 1.80 3.47 1.04)))))
(println *pc:scales*)
(set! *scale* (pc:scale 0 'gamelan-1))
(println (set! *scale* (pc:scale 1.97 'gamelan-1))) ; (0 1.970000 3.770000 7.240000 8.280000)

I even played around with the idea of naming of the notes of the scale "the notes are called “ding”, “dong”, “deng”, “dung” and “dang”) "
Minoru I think you may have knowledge of traditional scales of Japan which could be played by these "G-pc:functions".
There are many new things to try.
When I get time I'll try to put it all together a "G-pc_ivl.xtm" -- pitch class and interval sets that include real midi numbers. 
I already have a version which I called "MY_pc_ivl_copy.xtm" and includes modifications we have been discussing. 
I will save it in "extempore/libs/contrib/G-pc_ivl.xtm".
Maybe you have already done something like this?

I'm looking forward to making some interesting sounds.
Regards
George

Minoru

unread,
Jan 28, 2023, 11:49:41 PM1/28/23
to Extempore
 George,

;; I noticed something .....

;; about pc:pc

(pc:pc 50);2
(pc:pc 74);2

;; these return value 2 means tone D,
;; that is, one of D1, D2, D3 ... Dn,
;; (this case is D3 and D5 because of 50 and 74 midi note number)
;; D1 pitch(frequency) is different from D2 pitch, but same tone, octave tone,
;; so D1 and D2 has the same pictch class, it's 2,
;; that is, this 'pc:pc returns pitch class (or pitch class value/number ?),
;; pitch class is represented in 0 ,1, 2, 3 .... 11, whole(12) tone set,
;; instead of C, C#, D, D# ....... B
;; then we can calculate them because they are not charactor but number ....


;; about pc:degree

(pc:degree 50 '(0 1 2 3 4 5 6 7 9 10));3
(pc:degree 74 '(0 1 2 3 4 5 6 7 9 10));3
(pc:degree 50 '(0 2 3 4 5 6 7 9 10)) ;2
(pc:degree 74 '(0 2 3 4 5 6 7 9 10)) ;2
(pc:degree 50 '(0 1 3 4 5 6 7 9 10)) ;f
(pc:degree 74 '(0 1 3 4 5 6 7 9 10)) ;f

;; it returns degree in a scale which is represented by pc set,
;; this return value is not always same, it changes according to a scale/pc set(2nd argument), because of a degree.

;; On the other hand, return value of 'pc:pc is always same,
;; because it's pitch class(value/number).
;; I think 'pc:pc has its meaning.

;; I thougt above things in my poor knowledge.
;; tone/note/sound/pitch/ ...... music term in English ...
;; Anyone who knows more .....

2023年1月28日土曜日 7:27:13 UTC+9 George:
Minoru 

Minoru

unread,
Jan 29, 2023, 3:15:10 AM1/29/23
to Extempore
George

;; Thank you for your posting before.....
;;   I resolve the diffence of computer internal representation....
;;
;; pc:degree is modified to accept real besides integer
;;

(define G-pc:degree
    (lambda (value pc)
        (let ((r-val (floor (* 100 (modulo value 12)))))
            (let loop ((i 1)
                             (lst (map (lambda (x) (floor (* 100 x)))
                                              pc)))
                (if (null? lst)
                     (begin
                         (log-info "pitch not in pc") #f)
                     (if (= (car lst) r-val)
                          i
                         (loop (+ i 1) (cdr lst) )))))))

(G-pc:degree 60   '(0 2 4 5 7));1
(G-pc:degree 64   '(0 2 4 5 7));3
(G-pc:degree 61.1 '(0 1.1 2 4 5 7));2
(G-pc:degree 49.1 '(0 1.1 2 2.2 4 5 7));2
(G-pc:degree 50.2 '(0 1.1 2 2.2 4 5 7));4
(G-pc:degree 74.2 '(0 1.1 2 2.2 4 5 7));4

(pc:degree 74.2   '(0 1.1 2 2.2 4 5 7));f
(pc:degree 74     '(0 1.1 2 2.2 4 5 7));3
(G-pc:degree 74   '(0 1.1 2 2.2 4 5 7));3

Regards
2023年1月24日火曜日 13:50:43 UTC+9 George:

Minoru

unread,
Jan 29, 2023, 6:57:29 AM1/29/23
to Extempore
;;   I resolve the diffence of computer internal representation....
to 
;;   ......................difference of ........

2023年1月29日日曜日 17:15:10 UTC+9 Minoru:

Minoru

unread,
Jan 30, 2023, 3:00:08 AM1/30/23
to Extempore
George,

;; I modified G-pc:degree to have option arguments.
;;
;; option is 2 arguments,
;; 1: number of decimals (default 2)
;; 2: tone number (default 12, usual PC set)
;; Caution :  option is just 1 argumet(1:number of decimals) or
;;                   just 2 arguments(both 1:number of decimals and 2:tone number),
;;                   it's impossible to set just only 2:tone number.

(define G-pc:degree
    (lambda (value pc . opt)
        (let* ((dms (if (null? opt) 2 (car opt)))
                  (tn (if (or (null? opt)(null? (cdr opt))) 12
                                   (cadr opt)))
                  (f (lambda (x) (round (* x (expt 10 dms)))))
                  (r-val (f (modulo value tn))))
           (let loop ((i 1)
                           (lst (map (lambda (p) (f p))

                                            pc)))
               (if (null? lst)
                    (begin
                        (log-info "pitch not in pc") #f)
                    (if (= (car lst) r-val)
                         i
                        (loop (+ i 1) (cdr lst) )))))))


;; examples ...

(G-pc:degree 72 '(0 2 4 5 6));1
(G-pc:degree 72 '(0 2 4 5 6) 0 7);2
(G-pc:degree 60 '(0 2 4 5 6) 0 7);3

;; returns a degree in a given scale, normal PC set (12 tone)
(G-pc:degree 0  '(0 2 4 5 6));1
(G-pc:degree 3  '(0 2 4 5 6));f
(G-pc:degree 4  '(0 2 4 5 6));3
(G-pc:degree 7  '(0 2 4 5 6));f
(G-pc:degree 12 '(0 2 4 5 6));1
(G-pc:degree 14 '(0 2 4 5 6));2
(G-pc:degree 15 '(0 2 4 5 6));f
(G-pc:degree 16 '(0 2 4 5 6));3
(G-pc:degree 18 '(0 2 4 5 6));5
(G-pc:degree 20 '(0 2 4 5 6));f
(G-pc:degree 21 '(0 2 4 5 6));f

;; transformational PC set (7 tone, 0,1,2,3,4,5,6)
;; returns a degree in a given scale,
(G-pc:degree 0  '(0 2 4 5 6) 0 7);1
(G-pc:degree 3  '(0 2 4 5 6) 0 7);f
(G-pc:degree 4  '(0 2 4 5 6) 0 7);3
(G-pc:degree 7  '(0 2 4 5 6) 0 7);1
(G-pc:degree 12 '(0 2 4 5 6) 0 7);4
(G-pc:degree 14 '(0 2 4 5 6) 0 7);1
(G-pc:degree 15 '(0 2 4 5 6) 0 7);f
(G-pc:degree 16 '(0 2 4 5 6) 0 7);2
(G-pc:degree 18 '(0 2 4 5 6) 0 7);3
(G-pc:degree 20 '(0 2 4 5 6) 0 7);5
(G-pc:degree 21 '(0 2 4 5 6) 0 7);1

;; number of decimals


(pc:degree 74.2   '(0 1.1 2 2.2 4 5 7));f
(pc:degree 74     '(0 1.1 2 2.2 4 5 7));3
(G-pc:degree 74   '(0 1.1 2 2.2 4 5 7));3

(G-pc:degree 74.22 '(0 1.1 2 2.2  4 5 7));f
(G-pc:degree 74.22 '(0 1.1 2 2.22 4 5 7));4

(G-pc:degree 74.2  '(0 1.1 2 2.2  4 5 7));4

(G-pc:degree 74.22 '(0 1.1 2 2.2  4 5 7));f
(G-pc:degree 74.22 '(0 1.1 2 2.2  4 5 7) 1);4  

(G-pc:degree 60   '(0 2 4 5 7));1
(G-pc:degree 64   '(0 2 4 5 7));3
(G-pc:degree 61.1 '(0 1.1 2 4 5 7));2
(G-pc:degree 49.1 '(0 1.1 2 2.2 4 5 7));2
(G-pc:degree 50.2 '(0 1.1 2 2.2 4 5 7));4
(G-pc:degree 74.2 '(0 1.1 2 2.2 4 5 7));4
(G-pc:degree 73.2 '(0 1.1 2 2.2 4 5 7));f

(G-pc:degree 74.223 '(0 1.1 2 2.223 4 5 7));4
(G-pc:degree 74.223 '(0 1.1 2 2.222 4 5 7));4, accepts 2 number of decimals by default
(G-pc:degree 74.223 '(0 1.1 2 2.222 4 5 7) 3);f, accepts 3 number of decimals by option

Regards
2023年1月29日日曜日 17:15:10 UTC+9 Minoru:
George

Minoru

unread,
Jan 30, 2023, 7:08:30 AM1/30/23
to Extempore
figure for last posting

0 1 2 3 4 5 6 7 8 9 10 11 // 12 tone
12 13 14       .... ......    23   2nd circle
24 25 .............                 3rd
36 ....
48 ....
60 61 62 ....


0 1 2 3 4 5 6   // 7 tone
7 8 9 ...12 13    2nd circle
14 15 16  .... 3rd circle
21 ....  4th
28 ....   5th

2023年1月30日月曜日 17:00:08 UTC+9 Minoru:

George

unread,
Jan 30, 2023, 4:38:16 PM1/30/23
to Extempore
;; Minoru
;; I think I may have found a different way to do define G-pc:degree

;; Zero based list-position is defined in ~/extempore/runtime/scheme.xtm #1090
(list-position 5 '( 7 8 9 5 11 23)) ;; 3
(list-position 5.3 '( 71.3 8.4 9.7 5.3 11 23)) ;; 3
(list-position 5.3 '( 71.3 5.3 8.4 9.7 11 23)) ;; 1
(list-position 0 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 0
(list-position 1.970000 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 1
(list-position 3.770000 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 2
(list-position 7.24 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 3
(list-position 7.25 '(0 1.970000 3.770000 7.240000 8.280000)) ;; #f

;; One based list-position 
(+ 1 (list-position 5 '( 7 8 9 5 11 23))) ;; 4
(+ 1 (list-position 5.3 '( 71.3 8.4 9.7 5.3 11 23))) ;; 4
(+ 1 (list-position 5.3 '( 71.3 5.3 8.4 9.7 11 23))) ;; 2
(+ 1 (list-position 0 '(0 1.970000 3.770000 7.240000 8.280000))) ;; 1
(+ 1 (list-position 1.970000 '(0 1.970000 3.770000 7.240000 8.280000))) ;; 2
(+ 1 (list-position 3.770000 '(0 1.970000 3.770000 7.240000 8.280000))) ;; 3
(+ 1 (list-position 7.24 '(0 1.970000 3.770000 7.240000 8.280000))) ;; 4
(+ 1 (list-position 7.20 '(0 1.970000 3.770000 7.240000 8.280000))) ;; error

;; returns the one-based position of a value in a pc list
(define G-pc:degree
(lambda (value pc)
(if (null? pc)
'()
(if (pc:? value pc)
(+ (list-position (round (* 100 (modulo value 12))) (map by100 pc)) 1)
#f))))

(G-pc:degree 5 '()) ;; NIL
(G-pc:degree 5 '( 7 8 9 5 11 23)) ;; 4
(G-pc:degree 5.3 '( 71.3 8.4 9.7 5.3 11 23)) ;; 4
(G-pc:degree 5.3 '( 71.3 5.3 8.4 9.7 11 23)) ;; 2
(G-pc:degree 0 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 1
(G-pc:degree 1.970000 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 2
(G-pc:degree 3.770000 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 3
(G-pc:degree 67.24 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 4
(G-pc:degree 55.24 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 4
(G-pc:degree 56.28 '(0 1.970000 3.770000 7.240000 8.280000)) ;; 5
(G-pc:degree 68.27 '(0 1.970000 3.770000 7.240000 8.280000)) ;; #f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Minoru
I haven't looked  at your previous few posts but on an earlier one I found what I think is an inconsistency.
It goes like this: If you test a  single decimal place value (eg 65.3) then test for a double decimal place value (eg 63.77) and then
revert to testing single decimal place value, again, the second decimal place hangs around and distorts the result .
I'll try to reproduce the effect for you. You may already have fixed it in later versions.
Anyhow this led me on a chase to find an alternative definition for G-pc:degree.
Finding the list-position code already in scheme made it easy.

Regards
George

Minoru

unread,
Jan 30, 2023, 11:40:34 PM1/30/23
to Extempore
;; George,
;;
;; Yes, I fixed it, because I should use 'round instead of 'floor.
;;
;; Anyway, I made G-pc:degree by modifying original one.
;; On the other hand, your G-pc:degree is more simple than original one,
;; I think it's a nice one !
;; Then, I noticed that your G-pc:degree becomes more simple again.
;;   need not 'null? and 'pc:? check


(define G-pc:degree
    (lambda (value pc)
        (let ((ans (list-position (round (* 100 (modulo value 12))) (map by100 pc))))
            (if ans (+ 1 ans) #f))))

;; Now I modifyed it again, having optional argument,
;; in order to accept changing number of decimals and number of tone,
;; like my last G-pc:degree.
;;   need not any help functions including 'pc:?, 'by100
;; and the results are exactly the same.

;;
;; option is 2 arguments,
;; 1: number of decimals (default 2)
;; 2: tone number (default 12, usual PC set)
;; Caution: option is just 1 argumet(1:number of decimals) or
;;          just 2 arguments(both 1:number of decimals and 2:tone number)
;;          it's impossible to set just only 2:tone number.


(define G-pc:degree
    (lambda (value pc . opt)
        (let* ((dms (if (null? opt) 2 (car opt)))
                  (tn (if (or (null? opt)(null? (cdr opt))) 12 (cadr opt)))
                  (f (lambda (x) (round (* x (expt 10 dms)))))
                  (ans (list-position (f (modulo value tn)) (map f pc))))
            (if ans  (+ 1 ans)  (begin (log-info "pitch not in pc") #f))))) ; print it like original one

(G-pc:degree 74.22 '(0 1.1 2 2.2  4 5 7));f
(G-pc:degree 74.22 '(0 1.1 2 2.2  4 5 7) 1);4, number of significant digits is 1

(G-pc:degree 74.223 '(0 1.1 2 2.223 4 5 7)     ) ; 4
(G-pc:degree 74.223 '(0 1.1 2 2.222 4 5 7)     ) ; 4, number of significant digits is 2, by default
(G-pc:degree 74.223 '(0 1.1 2 2.222 4 5 7)  3 ) ; f,  number of significant digits is 3
2023年1月31日火曜日 6:38:16 UTC+9 George:

George Wright

unread,
Jan 31, 2023, 12:15:50 AM1/31/23
to extemp...@googlegroups.com
Looks good. Will test it out 
🥸
George J Wright 

On 31 Jan 2023, at 3:40 pm, Minoru <mils...@gmail.com> wrote:

;; George,
--
You received this message because you are subscribed to the Google Groups "Extempore" group.
To unsubscribe from this group and stop receiving emails from it, send an email to extemporelan...@googlegroups.com.

George

unread,
Jan 31, 2023, 3:04:02 AM1/31/23
to Extempore
I also found some interesting things about modulo:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interesting stuff about modulo
(modulo 26 12) ;; 2
(modulo (* 26 12) 12) ;; 0
(modulo (* 26 120) 12) ;; 0
(modulo (* 26 10) 12) ;; 8
(modulo (* 26 100) 12) ;; 8

(= (modulo (* 26 100) 12)  (* (modulo 26 12) (modulo 100 12))) ; #t

(= (modulo (* 26 50) 12)  (* (modulo 26 12) (modulo 50 12))) ; #t
(= (modulo (* 26 73) 12)  (* (modulo 26 12) (modulo 73 12))) ; #t

;; So maybe (modulo (* a b) c)  =  (* (modulo a c)  (modulo b c))

Must look up "Chinese remainder theorem " again!

George

Minoru

unread,
Jan 31, 2023, 6:22:25 AM1/31/23
to Extempore

click
>Properties (identities)
>In programming languages
        scheme's modulo and remainder

2023年1月31日火曜日 17:04:02 UTC+9 George:

George

unread,
Jan 31, 2023, 3:18:49 PM1/31/23
to Extempore
  • Distributive:
    • (a + b) mod n = [(a mod n) + (b mod n)] mod n.
    • ab mod n = [(a mod n)(b mod n)] mod n.
I should have written:
(modulo (* a b) c)  =  { (* (modulo a c)  (modulo b c))  mod  c }

George

unread,
Jan 31, 2023, 3:32:33 PM1/31/23
to Extempore
Minoru
I notice there are many versions of xtlang modulo that we could look at .......

Have fun!
George

George

unread,
Feb 1, 2023, 4:53:42 PM2/1/23
to Extempore
Minoru
I think I prefer your earlier version of G-pc:degree without the options for number of decimals
and number of tones in the "octave".

(define G-pc:degree
    (lambda (pitch pc)
        (let ((res (list-position (round (* 100 (modulo pitch 12))) (map by100 pc))))
            (if res (+ 1 res) #f))))

By the way I changed your "value" to "pitch" and "ans" to "res" to be consistent with
terminology used in other extempore code. And in other places I changed "opt" to "args" for the same reason. I hope you don't mind.

The options I think are an unnecessary complication that might not be very useful.
I think even the second decimal place might be a bit of an over-kill. 
I'm not sure if I can hear the difference between these:
(play-note (now) synth 70.00 80 (* 2.10 *second*))
(play-note (now) synth 70.05 80 (* 2.10 *second*))
But my hearing is poor. Others might be able to hear the difference.
Where the second decimal place is interesting is when the sounds are played together
and you get nice interference.
I haven't finished the task of putting all the new code together but will work on it this week.
Regards
George

Minoru

unread,
Feb 2, 2023, 2:55:56 AM2/2/23
to Extempore
George

>>> By the way I changed your "value" to "pitch" and .....

Oh it's ok, I just modified something on your code.

About decimals, I have been used it to make sounds like vibrato, using 2 or 3 sounds that has very little difference of pitch at same time or delaying 1 sounds, etc ....
Then I noticed that midi note differences that could affect this vibrato is from 1 decimal place to 4 decimal places !!  Yes, even 4 decimal places could make some differences of vibrato effecs, maybe more ???  Of course, it depends on pitch, volume, duration, number of sounds or chord etc ...

So I thougt the default number was 2, based on your functions, and added it as optional argument.
If just needed, just recall and use it.

It seems that we can not only hear the sound but also feel it, as pressure, vibration, etc, that is, the sound couldn't be hear has its meaning.

P.S.
When making "vibrato", I added some value to some of pitches made by original pc:make-chord, so I just wrote that how was it to add it to the returned value by pc:any-func .....

You seem to love gamelan music, kalau saya lebih suka gamelan Jawa daripada gamelan Bali. 

Regards

2023年2月2日木曜日 6:53:42 UTC+9 George:

George

unread,
Feb 2, 2023, 3:39:57 PM2/2/23
to Extempore
OK Minoru that's very interesting about making "vibrato" and other effects using 2,3,4 decimal places.
I can see the value of that.
It is something I had hoped would happen when using real midi notes.
Could you send me a few examples that you have tried.
As for gamelan music, I am far from being an expert. I certainly can't speak or read Indonesian.
I had to use google translate to read your question!
 I really chose gamelan because I had some information
on the relative spacing of the pentatonic scales and I used it as a general representation of the many music
traditions outside the usual European.
 In the original pc_ivl.xtm
;; various scales defined as pc sets 
(define *pc:scales*
  '((blues . (2 1 1 3 2))
.
 (gamelan-1 . (1.97 1.80 3.47 1.04)))). ;; I have defined a scale in *pc:scales*

I imagine once I have the information about spacings, we can add many other scales from many other parts of the world.
Regards
George

Minoru

unread,
Feb 3, 2023, 3:02:22 AM2/3/23
to Extempore
;; George,

;; It took a little while to rewrite these for extrempore v 0.89 from v 0.70.
;; I hope you can distinguish these sounds.

(sys:load "libs/core/instruments.xtm")

;; from Doc, changed something though .....
(bind-func osc4_c
  (lambda (phase)
    (lambda (amp freq:SAMPLE)
      (let ((incr (* 2.0 3.1415 (/ freq 44100.))))
        (set! phase (% (+ phase incr) (* 2.0 3.1415)))
        (* amp (sin phase))))))

(bind-func inst4_note
  (lambda ()
    (let ((amp_env:|4,float| (array 10.0:f 50.0 0.6 200.0)))
      (lambda (data:NoteData* nargs:i64 dargs:SAMPLE*)
        (let ((starttime (note_starttime data))
              (frequency (note_frequency data))
              (amplitude (note_amplitude data))    
              (duration (note_duration data))
              (a (aref amp_env 0))
              (d (aref amp_env 1))
              (s (aref amp_env 2))
              (r (aref amp_env 3))
              (release (convert (* SRs (convert (/ (aref amp_env 3) 1000.0))) i64))
              (o1 (osc4_c 0.0))
              (env (adsr_c))
              (eamp 0.0:f)
              (out:SAMPLE 0.0))
          (lambda (time:i64 chan:i64) ; infreq:SAMPLE inamp:SAMPLE)
            (if (= chan 0)
                (begin
                  (if (> (- time starttime) duration) (note_gate data 0.0))
                  (set! eamp (env chan (note_gate data) a d s r))
                  (if (> (- time starttime) (+ duration release)) (note_active data #f))
                  (set! out (o1 (* 1.0 (* eamp amplitude))
                                frequency))))
            (* 1.0 out)))))))

(bind-func inst4_fx
  (lambda ()
    (let ((notekernel:NOTE_KERNEL null))
      (lambda (in:SAMPLE time:i64 chan:i64 dat:SAMPLE*)
        in))))


(make-instrument inst4 inst4)

(bind-func dsp:DSP 300000
    (lambda (in time chan dat)
      (inst4 in time chan dat)))

(dsp:set! dsp)

;; play test
(play-note (now) inst4 70 60 (* *second* 1))
(play-note (now) inst4 70.1 60 (* *second* 1))

(begin
  (*metro* 'set-tempo 120)
  (set_amp_env inst4 1500.0 100 1. 2000.))


;; sound examples of effects like vibrato

;; 1. just same sounds
(let ((p1 70) (p2 70) (p3 70)
        (t1 (*metro* 'get-beat )) (d 6) )  
  (play-note (*metro* t1) inst4 p1 60 (*metro* 'dur d))
  (play-note (*metro* t1) inst4 p2 60 (*metro* 'dur d))
  (play-note (*metro* t1) inst4 p3 60 (*metro* 'dur d)))

;; 2. slow vibrato
(set_amp_env inst4 1500.0 100 1. 2000.)
(let ((p1 70) (p2 70) (p3 70.1)
        (t1 (*metro* 'get-beat )) (d 6) )  
  (play-note (*metro* t1) inst4 p1 60 (*metro* 'dur d))
  (play-note (*metro* t1) inst4 p2 70 (*metro* 'dur d))
  (play-note (*metro* (+ 1 t1)) inst4 p3 75 (*metro* 'dur (- d 1))))

;; 3. faster vibrato
(set_amp_env inst4 1500.0 100 1. 2000.)
(let ((p1 70) (p2 70) (p3 70.16)
        (t1 (*metro* 'get-beat )) (d 6))  
  (play-note (*metro* t1) inst4 p1 60 (*metro* 'dur d))
  (play-note (*metro* t1) inst4 p2 70 (*metro* 'dur d))
  (play-note (*metro* (+ 3/2 t1)) inst4 p3 75 (*metro* 'dur (- d 3/2))))

;; 4. more faster
(set_amp_env inst4 1500.0 100 1. 2000.)
(let ((p1 70) (p2 70) (p3 70.26)
        (t1 (*metro* 'get-beat )) (d 6) )  
  (play-note (*metro* t1) inst4 p1 60 (*metro* 'dur d))
  (play-note (*metro* (+ 1 t1)) inst4 p2 70 (*metro* 'dur (- d 1)))
  (play-note (*metro* (+ 3/2 t1)) inst4 p3 75 (*metro* 'dur (- d 3/2))))

;; 5. and changing at the ending of sound
(let ((p1 70) (p2 70.008) (p3 70.26)
        (t1 (*metro* 'get-beat )) (d 6) )  
  (play-note (*metro* t1) inst4 p1 60 (*metro* 'dur d))
  (play-note (*metro* t1) inst4 p2 70 (*metro* 'dur d))
  (play-note (*metro* t1) inst4 p3 75 (*metro* 'dur d)))

;; 6. this ends when the sound wave ends or downs
(begin
  (set_amp_env inst4 1500.0 100 1. 30.)

  (let ((p1 70) (p2 70.1) (p3 70.14)
          (t1 (*metro* 'get-beat )) (d 4) )  
    (play-note (*metro* t1) inst4 p1 60 (*metro* 'dur d))
    (play-note (*metro* t1) inst4 p2 70 (*metro* 'dur d))
    (play-note (*metro* t1) inst4 p3 75 (*metro* 'dur d))))

;; 7. this ends when the sound wave started, the attack time began ? ,,,,
;; having the edgy/clear stop, like applosive.
;; but really due to wave interference, I guess so ...
(begin
  (set_amp_env inst4 1500.0 100 1. 30.) ; sama as the above one

  (let ((p1 70) (p2 70.1088) (p3 70.1488)
          (t1 (*metro* 'get-beat )) (d 4) )  
    (play-note (*metro* t1) inst4 p1 60 (*metro* 'dur d))
    (play-note (*metro* t1) inst4 p2 70 (*metro* 'dur d))
    (play-note (*metro* t1) inst4 p3 75 (*metro* 'dur d))))

Regards
2023年2月3日金曜日 5:39:57 UTC+9 George:
Reply all
Reply to author
Forward
0 new messages