4
\$\begingroup\$

The following procedures are an attempt to connect some physical aspects of the sound (frequency and duration) to traditional music theory.

Each procedure is accompanied by one or more examples.

The names of the procedures are in Italian, but thanks to the examples should be understandable to those who have a basic knowledge of music.

to accordoDaListaAltezze :lista_altezze 
  localmake "lista_note map [daAltezzaANota ?] :lista_altezze
  OP accordoDaListaNote :lista_note
  ; es. accordoDaListaAltezze [do4 sol5 mi3 la4]      -> [la do mi sol]
  ; es. accordoDaListaAltezze [dob4 sol#5 mi3 labb4]  -> [labb dob mi sol#]
end

to accordoDaListaNote :lista_note
  localmake "note remdup :lista_note
  if (count :note) < 3 [OP "false]
  localmake "buone (list (first :note))
  localmake "candidate butfirst :note
  localmake "risultato promuoviCandidate (list :buone :candidate) (count :candidate)
  OP first :risultato
  ; es. accordoDaListaNote [do sol mi la] -> [la do mi sol]
end

to accordoDelGradoDelModoNote :grado :num_note :modo_note 
  localmake "modo_base_note daModoAModoBase :modo_note
  if or (:num_note < 3) (:num_note > 7) [OP [il numero di note deve essere compreso tra 3 e 7]] 
  localmake "fondamentale item :grado :modo_base_note
  localmake "seq prendi :num_note sequenzaPerGenerareAccordi
  OP map [succCircolareEnnesimo ? :fondamentale :modo_base_note] :seq
  ; es. accordoDelGradoDelModoNote 2 5 [sib do re mib fa sol la sib]
  ; es. accordoDelGradoDelModoNote 5 4 (daModoAModoNomi "do modoMaggioreAscendente)
  ; es. accordoDelGradoDelModoNote 2 3 (daModoAModoNomi "do modoMaggioreDiscendente)
  ; es. accordoDelGradoDelModoNote 5 4 (daModoAModoNomi "sib modoMaggioreAscendente)
end

to accordoListaAltezze? :lista_altezze 
  localmake "lista_note map [daAltezzaANota ?] :lista_altezze
  OP accordoListaNote? :lista_note
  ; es. accordoListaAltezze? [do4 sol5 mi3 la4] -> true
end

to accordoListaNote? :lista_note 
  localmake "note remdup :lista_note
  if (count :note) < 3 [OP "false]
  localmake "buone (list (first :note))
  localmake "candidate butfirst :note
  localmake "risultato promuoviCandidate (list :buone :candidate) (count :candidate)
  OP empty? (last :risultato)
  ; es. accordoListaNote? [do sol mi la] -> true
end

to aDistanzaDiTerzaDa? :nota1 :nota2 
  localmake "nota_base_1 daNotaANotaBase :nota1
  localmake "nota_base_2 daNotaANotaBase :nota2
  OP or ((succCircolareEnnesimo 2 :nota_base_1 nomiNoteBase) = :nota_base_2) ~
        ((predCircolareEnnesimo 2 :nota_base_1 nomiNoteBase) = :nota_base_2)
  ; aDistanzaDiTerzaDa? "do "la -> true
  ; aDistanzaDiTerzaDa? "la "do -> true
end

to alterazioneDaSpostamentoInSemitoni :n 
  if :n = 0  [OP "]
  if :n = 1  [OP "#]
  if :n = 2  [OP "x]
  if :n > 2  [OP word :n "#]
  if :n = -1 [OP "b]
  if :n = -2 [OP "bb]
  if :n < -2 [OP word (abs :n) "b]
  ; es. alterazioneDaSpostamentoInSemitoni 1  -> #
  ; es. alterazioneDaSpostamentoInSemitoni -2 -> bb
end

to altezzaPrecedente :nota :alterazione :ottava :spostamento_in_semitoni ; semitoni > 0 
  OP reduce [word ?1 ?2] (altezzaPrecedenteScomposta :nota :alterazione :ottava :spostamento_in_semitoni)
  ; es. altezzaPrecedente "do "# 4 -2  -> si3
  ; es. altezzaPrecedente "do "# 4 -1  -> si#3
end

to altezzaPrecedenteScomposta :nota :alterazione :ottava :spostamento_in_semitoni ; semitoni < 0 
  localmake "nota_precedente predCircolare :nota nomiNoteBase
  localmake "nuova_ottava ifelse :nota_precedente = "si [:ottava - 1] [:ottava]
  localmake "d1 distanzaInSemitoniDaDo0 :nota :alterazione :ottava
  localmake "d2 distanzaInSemitoniDaDo0 :nota_precedente "  :nuova_ottava
  localmake "delta :d1 - :d2 + :spostamento_in_semitoni
  localmake "nuova_alterazione alterazioneDaSpostamentoInSemitoni :delta
  OP (list :nota_precedente :nuova_alterazione :nuova_ottava)
  ; es. altezzaPrecedenteScomposta "do "# 4 -2  -> [si  3]
  ; es. altezzaPrecedenteScomposta "do "# 4 -1  -> [si # 3]
end

to altezzaSuccessiva :nota :alterazione :ottava :spostamento_in_semitoni ; semitoni > 0 
  OP reduce [word ?1 ?2] (altezzaSuccessivaScomposta :nota :alterazione :ottava :spostamento_in_semitoni)
  ; es. altezzaSuccessiva "do "# 4 1  -> re4
  ; es. altezzaSuccessiva "do "# 4 2  -> re#4
end

to altezzaSuccessivaScomposta :nota :alterazione :ottava :spostamento_in_semitoni ; semitoni > 0 
  localmake "nota_successiva succCircolare :nota nomiNoteBase
  localmake "nuova_ottava ifelse :nota_successiva = "do [:ottava + 1] [:ottava]
  localmake "d1 distanzaInSemitoniDaDo0 :nota :alterazione :ottava
  localmake "d2 distanzaInSemitoniDaDo0 :nota_successiva " :nuova_ottava
  localmake "delta :d1 - :d2 + :spostamento_in_semitoni
  localmake "nuova_alterazione alterazioneDaSpostamentoInSemitoni :delta
  OP (list :nota_successiva :nuova_alterazione :nuova_ottava)
  ; es. altezzaSuccessivaScomposta "do "# 4 1  -> [re  4]
  ; es. altezzaSuccessivaScomposta "do "# 4 2  -> [re # 4]
end

to altezzeDo0Si8 
  OP (crossmap [word ?2 ?1] nomiOttave nomiNoteConDiesis)
  ; altezzeDo0Si8  -> [do0 do#0 .. si8]
end

to baseModo :modo 
  OP butlast :modo
  ; es. baseModo modoMaggioreAscendente
  ; baseModo modoMaggioreAscendente         -> [0  2  4 5 7 9 11]
  ; baseModo modoMaggioreDiscendente        -> [12 11 9 7 5 4 2 ]
end

to concatena :lista 
  OP (reduce [word ?1 ?2] :lista)
  ; es. concatena [alter azione]  -> "alterazione
end

to daAltezzaAFrequenza :altezza 
  OP la4 * semitoni ((distanzaInSemitoniDaDo0DaAltezza :altezza) - distanzaInSemitoniDelLa4DaDo0Da)
  ; es. daAltezzaAFrequenza "la4   -> 440
  ; es. daAltezzaAFrequenza "la#4  -> 466.16376151809
  ; es. daAltezzaAFrequenza "lab4  -> 415.304697579945
  ; es. daAltezzaAFrequenza "la5   -> 880
  ; es. daAltezzaAFrequenza "la3   -> 220
  ; es. daAltezzaAFrequenza "solx4 -> 440
end

to daAltezzaANomeNota :altezza
  ifelse number? (last :altezza) [OP daAltezzaANomeNota (butlast :altezza)] [OP :altezza]
  ; es. daAltezzaANomeNota "la4   -> la
  ; es. daAltezzaANomeNota "la#4  -> la#
end

to daAltezzaANota :altezza
  OP rimuoviOttavaDaAltezza :altezza
  ; es. daAltezzaANota "la4   -> la
  ; es. daAltezzaANota "la#4  -> la#
end

to daAltezzeAFrequenze :altezze
  OP map [daAltezzaAFrequenza ?] :altezze
  ; es. daAltezzeAFrequenze [la4 la5 la3 solx4 sibb4 do4]  -> [440 880 220 440 440 261.625565300599]
end

to daAltezzeAInsiemeDiNote :altezze 
  OP remdup (daAltezzeANote :altezze)
  ; es. daAltezzeAInsiemeDiNote [do4 mi5 do3 sol4 mi4 la4 do5 sol5]  -> [mi la do sol]
end

to daAltezzeANote :altezze 
  OP map [daAltezzaANota ?] :altezze
  ; es. daAltezzeANote [do4 mi5 do3 sol4 mi4 la4 do5 sol5]  -> [do mi do sol mi la do sol]
end

to daFrequenzaAdAltezza :diapason :hz ; approssimazione al semitono più vicino 
  localmake "semitoni daFrequenzaASpostamentoInSemitoniDalDiapason :diapason :hz 
  OP item (1 + 57 + :semitoni) altezzeDo0Si8
  ; es. daFrequenzaAdAltezza 440 440  -> la4
  ; es. daFrequenzaAdAltezza 440 441  -> la4
  ; es. daFrequenzaAdAltezza 440 439  -> la4
  ; es. daFrequenzaAdAltezza 440 450  -> la4
  ; es. daFrequenzaAdAltezza 440 455  -> la#4
  ; es. daFrequenzaAdAltezza 440 880  -> la5
  ; es. daFrequenzaAdAltezza 440 220  -> la3
end

to daFrequenzaASpostamentoInSemitoniDalDiapason :diapason :hz ; approssimazione al semitono più vicino 
  localmake "cent round logBase (power 2 (1/1200)) (:hz / :diapason)
  localmake "smt int (:cent / 100)
  localmake "mc modulo :cent 100
  localmake "r ifelse :diapason <= :hz [ifelse :mc < 50 [0] [1]] [ifelse (or (:mc = 0) (:mc > 50)) [0] [-1]]
  OP :smt + :r
  ; es. daFrequenzaASpostamentoInSemitoniDalDiapason 440 440  -> 0
  ; es. daFrequenzaASpostamentoInSemitoniDalDiapason 440 441  -> 0
  ; es. daFrequenzaASpostamentoInSemitoniDalDiapason 440 450  -> 0
  ; es. daFrequenzaASpostamentoInSemitoniDalDiapason 440 455  -> 1
  ; es. daFrequenzaASpostamentoInSemitoniDalDiapason 440 880  -> 12
  ; es. daFrequenzaASpostamentoInSemitoniDalDiapason 440 220  -> -12
end

to daFrequenzeAdAltezze :diapason :lhz 
  OP map [daFrequenzaAdAltezza :diapason ?] :lhz
  ; es. daFrequenzeAdAltezze 440 [440 466 438 880 110 122]  -> [la4 la#4 la4 la5 la2 si2]
end

to daInteroAToniESemitoni :n ; n <> 0 
  if :n =   1 [OP "S]
  if :n =   2 [OP "T]
  if :n =   3 [OP "TS]
  if :n >=  4 [OP word :n "S]
  if :n =  -1 [OP "-S]
  if :n =  -2 [OP "-T]
  if :n =  -3 [OP "-TS]
  if :n <= -4 [OP word :n "S]
  ; es. daInteroAToniESemitoni 1   -> S
  ; es. daInteroAToniESemitoni 2   -> T
  ; es. daInteroAToniESemitoni -3  -> -TS
  ; es. daInteroAToniESemitoni 4   -> 4S
end

to daModoAModoBase :modo 
  OP butlast :modo
  ; es. daModoAModoBase modoMaggioreAscendente   -> [0 2 4 5 7 9 11]
  ; es. daModoAModoBase modoMaggioreDiscendente  -> [12 11 9 7 5 4 2]
end

to daModoAModoNomi :nomeNota :modo ; ad es. nomeNota può essere re oppure do# oppure la3b
  localmake "scala_altezze daModoAScalaAltezze (word :nomeNota "4) :modo
  OP map [daAltezzaANomeNota ?] :scala_altezze
  ; es. daModoAModoNomi "do modoMaggioreAscendente          -> [do re mi fa sol la si do]
  ; es. daModoAModoNomi "la modoMaggioreAscendente          -> [la si do# re mi fa# sol# la]
  ; es. daModoAModoNomi "reb modoMinoreArmonicoDiscendente  -> [reb do sibb lab solb fab mib reb]
end

to daModoAScalaAltezze :altezza :modo
  localmake "lista_altezza parseAltezza :altezza
  localmake "nota first :lista_altezza
  localmake "alterazione item 2 :lista_altezza
  localmake "ottava last :lista_altezza
  localmake "direzione ifelse (first :modo) = 0 ["ascendente] ["discendente]
  localmake "ottava_prima_altezza ifelse :direzione = "ascendente [:ottava] [:ottava + 1]
  localmake "prima_altezza (word :nota :alterazione :ottava_prima_altezza)
  localmake "spostamenti_modo spostamentiInSemitoniTraIGradiDelModo :modo
  localmake "altezze_successive ifelse :direzione = "ascendente ~
    [daSpostamentiAscendentiInSemitoniAdAltezze  :nota :alterazione :ottava_prima_altezza :spostamenti_modo []] ~
    [daSpostamentiDiscendentiInSemitoniAdAltezze :nota :alterazione :ottava_prima_altezza :spostamenti_modo []]
  OP sentence :prima_altezza :altezze_successive
  ; es. daModoAModoNomi "do modoMaggioreAscendente               -> [do re mi fa sol la si do]
  ; es. daModoAScalaAltezze "la4  modoMaggioreAscendente         -> [la4 si4 do#5 re5 mi5 fa#5 sol#5 la5]
  ; es. daModoAScalaAltezze "sib4 modoMinoreArmonicoDiscendente  -> [sib5 la5 solb5 fa5 mib5 reb5 do5 sib4]
end

to daNotaANotaBase :nota 
  localmake "2car prendiSottostringa 2 :nota
  OP ifelse :2car = "so ["sol] [:2car]
  ; es. daNotaANotaBase "do    -> do
  ; es. daNotaANotaBase "do#   -> do
  ; es. daNotaANotaBase "dobb  -> do
end

to daScalaAltezzeAModoNote :scala_altezze 
  OP rimuoviUltimiDuplicati (daAltezzeANote :scala_altezze)
  ; es. daScalaAltezzeAModoNote [do4 re4 mi4 fa4 sol4 la4 si4 do5 re5 mi5 fa5 sol5 la5 si5 do6]  -> [do re mi fa sol la si]
  ; es. daScalaAltezzeAModoNote [do6 si5 la5 sol5 fa5 mi5 re5 do5 si4 la4 sol4 fa4 mi4 re4 do4]  -> [do si la sol fa mi re]
end

to daSpostamentiAscendentiInSemitoniAdAltezze :nota :alterazione :ottava :lista_semitoni :buffer 
  if empty? :lista_semitoni [OP :buffer]
  localmake "altezza_successiva altezzaSuccessivaScomposta :nota :alterazione :ottava (first :lista_semitoni)
  localmake "nuovo_buffer SE :buffer (concatena :altezza_successiva) 
  localmake "nuova_nota item 1 :altezza_successiva
  localmake "nuova_alterazione item 2 :altezza_successiva
  localmake "nuova_ottava item 3 :altezza_successiva
  OP daSpostamentiAscendentiInSemitoniAdAltezze :nuova_nota :nuova_alterazione :nuova_ottava ~
       (butfirst :lista_semitoni) :nuovo_buffer
  ; es. daSpostamentiAscendentiInSemitoniAdAltezze "do "  "4 [2 2 1 2 2 2 1] []     -> [re4 mi4 fa4 sol4 la4 si4 do5]
  ; es. daSpostamentiAscendentiInSemitoniAdAltezze "do "  "4 [2 2 1 2 2 2 1] [do4]  -> [do4 re4 mi4 fa4 sol4 la4 si4 do5]
end

to daSpostamentiDiscendentiInSemitoniAdAltezze :nota :alterazione :ottava :lista_semitoni :buffer 
  if empty? :lista_semitoni [OP :buffer]
  localmake "altezza_precedente altezzaPrecedenteScomposta :nota :alterazione :ottava (first :lista_semitoni)
  localmake "nuovo_buffer SE :buffer (concatena :altezza_precedente) 
  localmake "nuova_nota item 1 :altezza_precedente
  localmake "nuova_alterazione item 2 :altezza_precedente
  localmake "nuova_ottava item 3 :altezza_precedente
  OP daSpostamentiDiscendentiInSemitoniAdAltezze :nuova_nota :nuova_alterazione :nuova_ottava ~
       (butfirst :lista_semitoni) :nuovo_buffer
  ; es. daSpostamentiDiscendentiInSemitoniAdAltezze "do "   "5 [-1 -2 -2 -2 -1 -2 -2] []      -> [si4 la4 sol4 fa4 mi4 re4 do4]
  ; es. daSpostamentiDiscendentiInSemitoniAdAltezze "do "   "5 [-1 -2 -2 -2 -1 -2 -2] [do5]   -> [do5 si4 la4 sol4 fa4 mi4 re4 do4]
  ; es. daSpostamentiDiscendentiInSemitoniAdAltezze "re "b  "5 [-1 -2 -2 -2 -1 -2 -2] [reb5]  -> [reb5 do5 sib4 lab4 solb4 fa4 mib4 reb4]
end

to diapason 
  OP 440
end

to distanzaInSemitoniDaDo0 :nota :alterazione :ottava 
  OP ((posizione :nota nomiNoteConDiesis) - 1) + (spostamentoInSemitoniDaAlterazione :alterazione) + (:ottava * 12)
  ; es. distanzaInSemitoniDaDo0 "do " 0  -> 0
  ; es. distanzaInSemitoniDaDo0 "do "# 0 -> 1
  ; es. distanzaInSemitoniDaDo0 "do "# 1 -> 13
  ; es. distanzaInSemitoniDaDo0 "la "b 4 -> 56
end

to distanzaInSemitoniDaDo0DaAltezza :altezza 
  localmake  "lista_nota parseAltezza :altezza
  localmake "nota first :lista_nota
  localmake "alterazione item 2 :lista_nota
  localmake "ottava last :lista_nota
  OP ((posizione :nota nomiNoteConDiesis) - 1) + (spostamentoInSemitoniDaAlterazione :alterazione) + (:ottava * 12)
  ; es. distanzaInSemitoniDaDo0DaAltezza "do0  -> 0
  ; es. distanzaInSemitoniDaDo0DaAltezza "do#0 -> 1
  ; es. distanzaInSemitoniDaDo0DaAltezza "do#1 -> 13
  ; es. distanzaInSemitoniDaDo0DaAltezza "lab4 -> 56
end

to distanzaInSemitoniDelLa4DaDo0
  OP distanzaInSemitoniDaDo0 "la " 4  ; = 57
end

to do3 
  OP la4 * (semitoni -21)  ; = 130.812782650299  (Hz)
end

to do5 
  OP la4 * (semitoni 3)  ; = 523.251130601197  (Hz)
end

to esempio1  
  suonaFrequenze (scala la4 modoMaggioreAscendente)
end

to esempio2 
  suonaFrequenze (scala la4 modoMaggioreDiscendente)
end

to esempio3 
  suonaFrequenze (scala do5 modoMaggioreDiscendente)
end

to esempio4 
  suonaFrequenze (scala do5 modoMinoreMelodicoAscendente)
  suonaFrequenze (scala do5 modoMinoreMelodicoDiscendente)
end

to esempio5 
  suonaFrequenze (scala sib3 modoMinoreMelodicoAscendente)
  suonaFrequenze (scala sib3 modoMinoreMelodicoDiscendente)
end

to esempio6 
  suonaFrequenze (scala do3 modoMinoreMelodicoAscendente)
  suonaFrequenze (scala do3 modoMinoreMelodicoDiscendente)
end

to esempio7 
  suonaFrequenze (scala la4 modoStranoAscendente)
  suonaFrequenze (scala la4 modoStranoDiscendente)
end

to frequenzaDellAltezza :alt 
  OP item (posizione :alt altezzeDo0Si8) frequenzeDo0Si8
end

to frequenzeDelleAltezze :lista_altezze 
  OP map [frequenzaAltezza ?] :lista_altezze
end

to frequenzeDo0Si8 
  localmake "num_semitoni 9*12 ; 9 ottave di 12 semitoni
  OP map [la4 * semitoni (? - 57)] (iseq 0 (:num_semitoni - 1)) ; 57 è la distanza in semitoni tra il la4 e il do0
  ; -> [16.3515978312874 17.3239144360545 18.354047994838 .. 7458.62018428944 7902.13282009799]
end

to gradiModo :modo 
  OP iseq 1 ((count :modo) - 1)
  ; es. gradiModo modoMaggioreAscendente   -> [1 2 3 4 5 6 7]
  ; es. gradiModo modoMaggioreDiscendente  -> [1 2 3 4 5 6 7]
  ; es. gradiModo modoStranoAscendente     -> [1 2 3 4 5]
  ; es. gradiModo modoStranoDiscendente    -> [1 2 3 4 5 6]
end

to la#4 
  OP la4 * (semitoni 1)  ; = 466.16376151809 (Hz)
end

to la4 
  OP diapason  ; = 440 (Hz)
end

to logBase :b :n 
  OP (ln :n) / ln :b
  ; es. logBase 2  16    -> 4
  ; es. logBase 10 1000  -> 3
  ; es. logBase (semitoni 1) (880/440)  -> 12
end

to modoMaggioreAscendente 
  ; sequenza degli "spostamenti" in semitoni dei gradi di una scala maggiore ascendente da una frequenza data
  OP [0 2 4 5 7 9 11 12]
end

to modoMaggioreDiscendente 
  ; sequenza degli "spostamenti" in semitoni dei gradi di una scala maggiore discendente da una frequenza data
  OP reverse modoMaggioreAscendente
end

to modoMinoreArmonicoAscendente 
  ; sequenza degli "spostamenti" in semitoni dei gradi di una scala maggiore ascendente da una frequenza data
  OP [0 2 3 5 7 8 11 12]
end

to modoMinoreArmonicoDiscendente 
  OP reverse modoMinoreArmonicoAscendente
end 

to modoMinoreMelodicoAscendente 
  ; sequenza degli intervalli tra i gradi di una scala minore melodica ascendente
  OP [0 2 3 5 7 9 11 12]
end

to modoMinoreMelodicoDiscendente 
  OP [12 10 8 7 5 3 2 0]
end

to modoMinoreNaturaleAscendente 
  ; sequenza degli intervalli tra i gradi di una scala minore naturale ascendente
  OP [0 2 3 5 7 8 10 12]
end

to modoMinoreNaturaleDiscendente 
  OP reverse  modoMinoreNaturale Ascendente
end

to modoStranoAscendente 
  OP [0 3 5 6 9 12]
end

to modoStranoDiscendente 
  OP [12 9 7 5 3 1 0]
end

to nomiNoteBase 
  OP [do re mi fa sol la si]
end

to nomiNoteConDiesis 
  OP [do do# re re# mi fa fa# sol sol# la la# si]
end

to nomiOttave 
  OP iseq 0 8  ; -> [0 1 2 3 4 5 6 7 8]
end

to notaDellAccordo? :nota :accordo  
  OP memberp :nota :accordo
  ; es. notaDellAccordo? "do [do mi sol]  -> true
  ; es. notaDellAccordo? "do [mi sol do]  -> true
  ; es. notaDellAccordo? "sol# [dob mi sol#]  -> true
end

to noteDellAccordo? :note :accordo 
  OP reduce "and (map [notaDellAccordo? ? :accordo] :note)
  ; es. noteDellAccordo? [dox sib] [mi# sib sol dox]  -> true
end

to numGradiModo :modo 
  OP count (gradiModo :modo)
  ; es. numGradiModo modoMaggioreAscendente      -> 7
  ; es. numGradiModo modoStranoAscendente        -> 5
  ; es. show numGradiModo modoStranoDiscendente  -> 6
end

to omofone? :altezza1 :altezza2 
  OP (distanzaInSemitoniDaDo0DaAltezza :altezza1) = (distanzaInSemitoniDaDo0DaAltezza :altezza2)
  ; es. omofone? "si#3 "do4    -> true
  ; es. omofone? "la4  "solx4  -> true
end

to parseAlterazione :stringa ; alterazioni "strane" solo se nel formato "d#" oppure "db", dove d = 1 cifra 
  localmake "1car first :stringa
  localmake "resto1 butfirst :stringa
  if member? :1car [# x] [OP list :1car :resto1]
  if :1car = "b [ifelse (first :resto1) = "b [OP list "bb bf :resto1]
    [OP list "b :resto1]]
  if (count :stringa) = 1 [OP list " :stringa]
  OP list (prendiSottostringa 2 :stringa) (togliDaStringa 2 :stringa)
end

to parseAltezza :alt ; alterazioni "strane" solo se nel formato "d#" oppure "db", dove d = 1 cifra 
  localmake "2car prendiSottostringa 2 :alt
  ifelse :2car = "so [localmake "nota "sol localmake "resto togliDaStringa 3 :alt] ~
    [localmake "nota :2car localmake "resto togliDaStringa 2 :alt]
  localmake "alterazione_e_ottava parseAlterazione :resto
  OP SE :nota :alterazione_e_ottava
  ; es. parseAltezza "do#4   -> [do # 4]
  ; es. parseAltezza "dobb2  -> [do bb 2]
  ; es. parseAltezza "do3#2  -> [do 3# 2]
end

to posizione :elemento :lista  
  OP 1 + (count :lista) - count (member :elemento :lista)
  ; es. posizione "do#3 [re4 mib5 do#3 re4 do#3]  -> 3
end

to predCircolare :elem :lista ; predecessore circolare 
  localmake "posiz posizione :elem :lista 
  OP ifelse :posiz = 1 [item (count :lista) :lista] [item (:posiz - 1) :lista]
  ; es. predCircolare "si [do re mi fa sol la si]  -> la
  ; es. predCircolare "do [do re mi fa sol la si]  -> si
end

to predCircolareEnnesimo :n :elem :lista 
  OP cascade (modulo :n (count :lista)) [predCircolare ? :lista] :elem
  ; es. predCircolareEnnesimo 3 "si [do re mi fa sol la si]  -> fa
  ; es. predCircolareEnnesimo 1 "do [do re mi fa sol la si]  -> si
  ; es. predCircolareEnnesimo 3 "do [do re mi fa sol la si]  -> sol
end

to prendi :n :lista
  if :n = 0 [OP []]
  if empty? :lista [OP []]
  OP (SE (first :lista) (prendi (:n - 1) (butfirst :lista)))
  ; es. prendi 2 [do4 sol#5 mi3 labb2]  -> [do4 sol#5]
end

to prendiSottostringa :n :stringa
  if :n = 0 [OP "]
  if :stringa = " [OP "]
  OP (word (first :stringa) (prendiSottostringa (:n - 1) (butfirst :stringa)))
  ; prendiSottostringa 3 "sol#4  -> sol
end

to promuoviCandidate :buone_candidate :max_tentativi 
  if :max_tentativi = 0 [OP :buone_candidate]
  localmake "buone first :buone_candidate
  localmake "candidate last :buone_candidate
  if empty? :candidate [OP :buone_candidate]
  localmake "candidata first :candidate
  if terzaAscendente?  (last :buone) :candidata ~
    [OP promuoviCandidate  (list (SE :buone :candidata) (butfirst :candidate)) :max_tentativi]
  if terzaDiscendente?  (first :buone) :candidata ~
    [OP promuoviCandidate  (list (SE :candidata :buone) (butfirst :candidate)) :max_tentativi]
  OP promuoviCandidate (list :buone (SE (butfirst :candidate) :candidata)) (:max_tentativi - 1)
end 

to rappresentazioneTradizionaleDelModo :modo 
  OP map [daInteroAToniESemitoni  ?] (spostamentiInSemitoniTraIGradiDelModo :modo)
  ; rappresentazioneTradizionaleDelModo modoMaggioreAscendente   -> [T T S T T T S]
  ; rappresentazioneTradizionaleDelModo modoMaggioreDiscendente  -> [-S -T -T -T -S -T -T]
end

to rimuoviOttavaDaAltezza :altezza 
  ifelse number? (last :altezza) [OP rimuoviOttavaDaAltezza (butlast :altezza)] [OP :altezza]
  ; es. rimuoviOttavaDaAltezza "do4    -> do
  ; es. rimuoviOttavaDaAltezza "dobb2  -> dobb
end

to rimuoviUltimiDuplicati :lista ; nub
  OP transfer [] [ifelse member? ?IN ?OUT [?OUT] [lput ?IN ?OUT]] :lista
  ; es. rimuoviUltimiDuplicati [1 2 3 1 1 4 6 3 1 2]  -> [1 2 3 4 6]
end

to scala :freq_base :modo 
  OP map [:freq_base * semitoni ? ] :modo
  ; es. scala la4 modoMaggioreAscendente = scala 440 modoMaggioreAscendente
  ; es. scala 440 modoMaggioreAscendente   -> [440 493.883301256124 554.365261953744 587.329535834815 659.25511382574 739.988845423269 830.60939515989 880]
  ; es. scala 440 modoMaggioreDiscendente  -> [880 830.60939515989 739.988845423269 659.25511382574 587.329535834815 554.365261953744 493.883301256124 440]
end

to scala2 :freq_base :modo :ottava 
  OP map [:freq_base * semitoni (? + 12 * :ottava)] :modo
  ; es. scala2 440 modoMaggioreAscendente 1   -> [880 987.766602512248 1108.73052390749 1174.65907166963 1318.51022765148 1479.97769084654 1661.21879031978 1760]
  ; es. scala2 440 modoMaggioreDiscendente 1  -> [1760 1661.21879031978 1479.97769084654 1318.51022765148 1174.65907166963 1108.73052390749 987.766602512248 880]
end

to scala4 :freq_base :modo :da_ottava :a_ottava 
  localmake "scl map.se [butlast (scala2 :freq_base (baseModo :modo) ?)] (iseq :da_ottava (:a_ottava - 1))
  OP SE :scl (scala2 :freq_base :modo :a_ottava)
  ; es. scala4 220 modoMaggioreAscendente 1 3
  ; es. scala4 220 modoMaggioreDiscendente 3 1
end

to scalaMaggioreLa4La5 
  OP [440 494 554 587 659 740 831 880]
  ;  = OP map [round (la4 * semitoni ?)] [0 2 4 5 7 9 11 12]
end

to semitoni :n 
  OP power 2 (:n/12)
  ; es. semitoni 0    -> 1
  ; es. semitoni 1    -> 1.0594630943593
  ; es. semitoni 2    -> 1.12246204830937
  ; es. semitoni 12   -> 2
  ; es. semitoni 24   -> 4
  ; es. semitoni -1   -> 0.943874312681694
  ; es. semitoni -2   -> 0.890898718140339
  ; es. semitoni -12  -> 0.5
  ; es. semitoni -24  -> 0.25
end

to sequenzaPerGenerareAccordi ; per un modo di 7 gradi 
  OP [0 2 4 6 1 3 5]
end

to sib3 
  OP la4 * ( semitoni -11 )  ; = 233.081880759045 (Hz) 
end

to spostamentiInSemitoniTraIGradiDelModo :modo ; fromModeToGradesDeplacementsInSemitones
  OP (map [?1 - ?2] (butfirst :modo) (butlast :modo))
  ; es. spostamentiInSemitoniTraIGradiDelModo modoMaggioreAscendente   -> [2 2 1 2 2 2 1]
  ; es. spostamentiInSemitoniTraIGradiDelModo modoMaggioreDiscendente  -> [-1 -2 -2 -2 -1 -2 -2]
  ; es. spostamentiInSemitoniTraIGradiDelModo modoMinoreMelodicoAscendente   -> [2 1 2 2 2 2 1]
  ; es. spostamentiInSemitoniTraIGradiDelModo modoMinoreMelodicoDiscendente  -> [-2 -2 -1 -2 -2 -1 -2]
end

to spostamentoAssolutoInSemitoni :nota1 :alterazione1 :ottava1 :nota2 :alterazione2: :ottava2 
  OP (distanzaInSemitoniDaDo0 :nota2 :alterazione2: :ottava2) - (distanzaInSemitoniDaDo0 :nota1 :alterazione1 :ottava1)
  ; es. spostamentoAssolutoInSemitoni "do "#  4 "re "   4  -> 1
  ; es. spostamentoAssolutoInSemitoni "re "   4 "do "#  4  -> -1
  ; es. spostamentoAssolutoInSemitoni "do "#  4 "re "#  5  -> 14
end

to spostamentoInSemitoniDaAlterazione :a 
  if :a = "   [OP 0]
  if :a = "#  [OP 1]
  if :a = "x  [OP 2]
  if :a = "b  [OP -1]
  if :a = "bb [OP -2]
  OP (butlast :a) * (ifelse (last :a) = "# [1] [-1])
  ; es. spostamentoInSemitoniDaAlterazione "#   -> 1
  ; es. spostamentoInSemitoniDaAlterazione "bb  -> -2
end

to succCircolare :elem :lista ; successore circolare 
  localmake "posiz posizione :elem :lista 
  OP ifelse :posiz = count :lista [item 1 :lista] [item (:posiz + 1) :lista]
  ; es. succCircolare "do [do re mi fa sol la si]  -> re
  ; es. succCircolare "si [do re mi fa sol la si]  -> do
end

to succCircolareEnnesimo :n :elem :lista 
  OP cascade (modulo :n (count :lista)) [succCircolare ? :lista] :elem
  ; es. succCircolareEnnesimo 3 "do [do re mi fa sol la si]  -> fa
  ; es. succCircolareEnnesimo 1 "si [do re mi fa sol la si]  -> do
  ; es. succCircolareEnnesimo 3 "si [do re mi fa sol la si]  -> mi
end

to suonaFrequenze :lhz ; fromHertzsToLogoSounds del modulo Logo 
  localmake "dms 700
  sound map.se [list (round ?) :dms] :lhz
  ; es. suonaFrequenze [440 466.16376151809 233.081880759045] = suonaFrequenze [la4 la#4 sib3] (solo se altezze definite)
  ; es. suonaFrequenze [220 440 880 1760]
end

to suonaFrequenzeConDurata :dms :lhz ; 1000 ms = 1 s
  sound map.se [list (round ?) :dms] :lhz
  ; es. suonaFrequenzeConDurata 1000 [440 466.16376151809 233.081880759045] = suonaFrequenze [la4 la#4 sib3] (solo se altezze definite)
  ; es. suonaFrequenzeConDurata 1500 [220 440 880 1760]
end

to terzaAscendente? :nota1 :nota2 
  localmake "nota_base_1 daNotaANotaBase :nota1
  localmake "nota_base_2 daNotaANotaBase :nota2
  OP (succCircolareEnnesimo 2 :nota_base_1 nomiNoteBase) = :nota_base_2
  ; es. terzaAscendente? "do  "mi   -> true
  ; es. terzaAscendente? "dob "mi#  -> true
  ; es. terzaAscendente? "mi  "do   -> false
end

to terzaDiscendente? :nota1 :nota2 
  OP terzaAscendente? :nota2 :nota1
  ; es. terzaDiscendente? "do  "la   -> true
  ; es. terzaDiscendente? "do# "lab  -> true
  ; es. terzaDiscendente? "la  "do   -> false
  ; es. terzaDiscendente? "do  "mi   -> false
  ; es. terzaDiscendente? "mi  "do   -> true
end

to togliDaStringa :n :stringa
  if :n = 0 [OP :stringa]
  if empty? :stringa [OP "]
  OP togliDaStringa (:n - 1) (butfirst :stringa)
  ; es. togliDaStringa 5 "alterazione"  -> "azione
end
\$\endgroup\$
11
\$\begingroup\$

The examples you included with nearly every procedure are very helpful. Nevertheless, this is a large library of about 100 procedures, which makes it hard to learn. Some simplifying strategies include:

  • Writing a synopsis or demo. A couple of interesting use cases, written as comments or unit tests, would help to give an overview of the goals of the library and how everything fits together.
  • Organizing the procedures by theme, and making an index. I see at least the following categories:

    • Generic helper functions (e.g. logBase, concatena, posizione, prendi, prendiSottostringa, predCircolare, …)
    • Note naming (e.g. alterazioneDaSpostamentoInSemitoni, altezzaPrecedente, altezzeDo0Si8, nomiNoteBase, nomiNoteConDiesis, doNotaANoteBase, …)
    • Intervals (e.g. daInteroAToniESemitoni, distanzaInSemitoniDaDo0, aDistanzaDiTerzaDa?, terzaAscendente?, spostamentoAssolutoInSemitoni, …)
    • Modes (e.g. baseModo, gradiModo, modoMaggioreAscendente, rappresentazioneTradizionaleDelModo, …)
    • Chords (e.g. accordoDaListaAltezze, accordoDaListaNote, notaDellAccordo?, noteDellAccordo?, promuoviCandidate, sequenzaPerGenerareAccordi, …)
    • Standard frequencies (diapason, do3, do5, la4, la#4, sib3, frequenzeDo0Si8)
    • Frequencies and scales (daModoAScalaAltezze, daScalaAltezzeAModoNote, semitoni, scala, scala2, scala4, scalaMaggioreLa4La5)
    • Audio (suonaFrequenze, suonaFrequenzeConDurata)
    • Examples (esempio, …, esempio7)
  • Reducing special notes. I can understand the need for diapason (aliased to la4), because that is the standard frequency upon which all tuning is based. I could also understand do4 (261.626 Hz, "middle do"). But do3, do5, la#4, and sib3? Those look like clutter to me.

    Along those lines, you have procedures named distanzaInSemitoniDaDo0, distanzaInSemitoniDaDo0DaAltezza, and distanzaInSemitoniDelLa4DaDo0. Why isn't there just a single procedure distanzaInSemitoni :altezzaDiReferenza :altezza?

  • Eliminating trivially derived procedures. For each procedure that returns a mode (modoMaggioreAscendente, modoMinoreArmonicoAscendente, modoMinoreMelodicoAscendente, …), you also have a corresponding …Discendente procedure. I would prefer shorter procedure names (such as modoMaggiore), with the understanding that it's easy to call reverse on the result if I want the descending scale.

    Similarly, there is little point in defining daAltezzeAFrequenze that is just a plural version of daAltezzaAFrequenza. You only need to offer one or the other. Bloating the library's interface for a little bit of convenience isn't worth it.

  • Eliminating redundancy and applying consistent naming. Some of your procedures are named according to the scheme daCosaXACosaY; others are just CosaYDaCosaX. One such redundant pair is daAltezzaAFrequenza and frequenzaDellAltezza — they appear to be independently implemented ways to achieve the same result.

    By the way, daAltezzaAFrequenza is broken: - distanzaInSemitoniDelLa4DaDo0Da should be - distanzaInSemitoniDelLa4DaDo0.

    I am also puzzled by the pair daAltezzaANomeNota and daAltezzaANota. The comments suggest that they behave identically.

  • Marking helper functions as such. Some functions, such as prendiSottostringa, altezzaPrecedenteScomposta, and daInteroAToniESemitoni, don't appear to be part of your library's interface. You could use some naming convention to indicate that these are "private" procedures.

I'm a bit confused by what exactly you consider a "nota". In daNotaANotaBase, for example, it's clear that "nota" means a note name including any accidental (♮, ♯, ♭, 𝄪, 𝄫), whereas "nota base" refers to the name with accidentals stripped off. On the other hand, why does the procedure altezzaPrecedente :nota :alterazione :ottava :spostamento_in_semitoni accept an :alterazione parameter separately from the :nota?

Despite the criticisms above, the functionality does seem reasonably clear for the most part, thanks to the long explicit names for procedures and parameters, along with the examples accompanying each procedure.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.