quarta-feira, 14 de maio de 2008

Geração de tablaturas em Scheme

O Pedro provavelmente vai me chamar de imbecil por eu estar apresentando um programa para geração de um tipo de notação musical tão primitiva, simplória e pouco representativa. Mas azar :-). Para analfabetos musicais como eu, tablaturas são uma forma simples de representar notas a serem tocadas em um instrumento.

O programa a seguir (em Chicken Scheme) gera tablaturas para contrabaixos de quatro cordas com duas oitavas por corda. Adaptar para guitarra/violão e para a notação de acordes não deve ser difícil.

(use (srfi 1))

(define (deriva-nota corda #!optional (desloc 0) oitava)
;; deriva notas a partir de corda e posicao de referencia
;; representando a nota mais grave
(let* ((corda-vazia (make-vector 4 '-))
(pos-corda-0 (+ desloc (if oitava 12 0)))
(pos (case corda
((0) pos-corda-0)
((1) (+ pos-corda-0 7))
((2) (+ pos-corda-0 2))
((3) (+ pos-corda-0 9)))))
(vector-set! corda-vazia corda
(if (and (>= pos 12) (not oitava))
(- pos 12)
pos))
(vector->list corda-vazia)))

(define-macro (cria-notas)
;; cria funcoes com nome correspondente `as notas
(let ((notas/desloc '((E . 0)
(F . 1)
(F# . 2)
(G . 3)
(G# . 4)
(Ab . 4)
(A . 5)
(A# . 6)
(Bb . 6)
(B . 7)
(C . 8)
(C# . 9)
(Db . 9)
(D . 10)
(D# . 11)
(Eb . 11))))
(append
'(begin)
(map (lambda (nota)
`(define (,(car nota) corda #!optional oitava)
(deriva-nota corda ,(cdr nota) oitava)))
notas/desloc))))

;; gera as definicoes das funcoes
(cria-notas)

(define (formata-corda corda notas)
(string-append
(case corda
((0) "G")
((1) "D")
((2) "A")
((3) "E"))
" |"
(string-intersperse
(map (lambda (nota)
(string-append
"--"
(->string nota)
(if (and (number? nota) (> nota 9))
"-"
"--")))
(map
(case corda
((0) cadddr)
((1) caddr)
((2) cadr)
((3) car))
notas))
"") "|"))

(define (tablatura . notas)
(define notas/corda 10) ;; numero maximo de notas por
;; linha da tablatura
(define (tab notas)
(for-each (lambda (corda)
(print (formata-corda corda notas)))
'(0 1 2 3)))
(let loop ((bloco notas))
(cond ((> (length bloco) notas/corda)
(tab (take bloco notas/corda))
(loop (drop bloco notas/corda)))
(else
(print "")
(tab (append ;; preenche ate' o final da linha
bloco
(make-list (- notas/corda (length bloco))
'(- - - -) )))))))

Para gerar uma tablatura, o usuário especifica as notas através de funções, e as passa como argumento para a função tablatura. As funções para representação de notas usam como argumentos a corda em que devem ser tocadas (de 0 a 3) e, opcionalmente, se uma oitava acima. Um exemplo com a escala pentatônica é mostrado abaixo:

(tablatura (G 0) (A# 0) (C 1) (D 1) (F 2))


G |--------------------------------------------------|
D |----------------------3---------------------------|
A |------------3----5--------------------------------|
E |--3----6------------------------------------------|

O programa quebra a linha da tablatura depois de um determinado número de notas (no código este parâmetro está com o valor 10) e completa a linha caso o número de notas seja menor que 10.

Uma tablatura com mais de 10 notas:

(tablatura
(G 0) (A# 0) (C 1) (D 1) (F 2)
(G 0) (A# 0) (C 1) (D 1) (F 2)
(G 0) (A# 0) (C 1) (D 1) (F 2))

G |--------------------------------------------------|
D |----------------------3------------------------3--|
A |------------3----5-------------------3----5-------|
E |--3----6-------------------3----6-----------------|

G |--------------------------------------------------|
D |----------------------3---------------------------|
A |------------3----5--------------------------------|
E |--3----6------------------------------------------|

Nenhum comentário: