ldenman / common-lisp-ear-training

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

This is my experminetal pet project and is not meant for public use IMHO.

You may be interested in some other Common Lisp music libraries below:

Background

“This program is ugly, dirty, disgusting, not the way I would write this as a professional. It is written with concrete syntax, meaning I’m going to use lots of cars and cdrs…” – Gerald Jay Sussman (https://youtu.be/aAlR3cezPJg?t=339)

This project is my exploration in representing musical information with LISP.

What kind of musical information is there to represent? To start… Notes, Scales, and Chords.

  • Notes - the most basic unit of musical pitch information. These are currently represented as integers (MIDI) and symbolically as moveable-do solfege like (DO RE MI) and absolute name “C4” where “C” is the note and “4” is the octave on the keyboard.
  • Scales - collections of notes based on scale patterns (major, minor, etc)
  • Chords - collections of notes usually derived from a scale

This program deals only with the notes on a standard 88 key piano as limiting the notes to 88 seems to be practical at this time.

High level ideas

The piano is a list of 88 notes. From this structure we can derive all sorts of musical structures.

I’m interested in tonal structures… the major and minor scales, diatonic chords derived from those scales, and ear training concepts.

In general, I want to create musical structure with commonly used symbols, the same ones used in music theory and ear training classes…

Specifically, I want solfege symbols to reference scale and chord tones, such as:

  • Ex.) The major scale in solfege: DO RE MI FA SO LA TI DO
  • Ex.) The nat. minor scale in solfege: DO RE ME FA SO SE LE TE DO
  • Ex.) The asc. chromatic scale: DO DI RE RI MI FA FI SO SI LA LI TI DO
  • Ex.) The desc. chromatic scale: DO TI TE LA LE SO SE FA MI ME RE RA DO

I also want symbols for referring to diatonic chords. In music theory and ear training chords are usually referred to by Roman Numerals:

  • Ex.) The most common chord progression: I V VI- IV
  • Ex.) The plagal cadence: I IV I
  • Ex.) The cycle V progression: I IV VII III- VI- III- V I

Dependencies

quicklisp

sbcl dependent

  • I am using SBCL and the #’schedule fn (in sequencer.lisp) spawns a thread. I believe the thread functionality is SBCL dependent.
  • The use of #’sb-ext:seed-random-state function is SBCL dependent

portmidi - Download it locally

This depends on the portmidi library to play midi in-memory to a hardware or software midi device of your choosing.

midi library - Download it locally

The MIDI library provides the ability to read/write midi files.

Setup

Qsynth settings

I am using the Qsynth software synth with following settings YMMV:

  • MIDI
    • MIDI DRIVER: alsa_seq
  • AUDIO
    • AUDIO DRIVER: jack

Load project

(ql:quickload "ld-music")
(in-package :ld-music)

Emacs Integrations

project reloading functions

I have these functions in my ~/.emacs file to quickly reload the project:

   (defun ld-music-reload ()
     (interactive)
     (ld-music-unload)
     (ld-music-load))

   (defun ld-music-unload ()
     (interactive)
     (shell-command "killall qsynth")
     (when (slime-connected-p)
	 (slime-eval '(ld-music:midi-close))))

   (defun ld-music-unload-hard ()
     (interactive)
     (when (slime-connected-p)
	 (slime-quit-lisp)
	 (sit-for 1)
	 (slime-kill-all-buffers)
	 (sit-for 1)))

   (defun ld-music-load ()
     (interactive)
     (start-process "qsynth" "qsynth" "qsynth")
     (if (not (slime-connected-p)) (slime))
     (sit-for 1)
     (when (slime-eval '(ql:quickload "ld-music"))
	 (sit-for 1)))

project testing functions

     (defun ld-music-runtests ()
	(interactive)
	(princ (slime-eval '(ld-music:run-tests))))

     (define-minor-mode ld-music-test-mode
	"ld-music-test-mode")

     (defun ld-music-test ()
	"run ld-music tests"
	(when ld-music-test-mode
	  (ld-music-runtest)))

     (add-hook 'after-save-hook #'ld-music-test)

other

(defun preview-midi (file)
  (interactive "fFile: ")
  (shell-command (format "midi2ly %s -o ~/output.midi.ly" file))
  (shell-command (format "lilypond --pdf -o ~/output.pdf ~/output.midi.ly"))
  (shell-command "evince ~/output.pdf.pdf"))
(defun preview-midi-output ()
(interactive)
  (preview-midi "~/output2.midi"))

MIDI Smoke testing

List the midi devices available on your system:

(pm:list-devices)

Find your MIDI device where output: T and copy the Integer. For example, my device is (2 . ALSA | Roland Digital Piano MIDI 1 | input: NIL | output: T)

Initialize the midi device and smoke test

(pm-initialize 2)

You should hear a note play when you run #’smoke-test.

(smoke-test)

DATA FORMATS

NOTE
an ALIST grouping TYPE, NAME, VALUE, SOLFEGE, OCTAVE
SCALE
an ALIST grouping NOTES and SCALE TEMPLATE
SCALE TEMPLATE
a list of scale steps/solfege pairs used to realize scales
CHORD
a list of CHORD-TONES
CHORD-TONE
an ALIST grouping TYPE, NOTE, DEGREE
SCALE-CHORD
an ALIST grouping SCALE, CHORDS, ROMAN-NUMERAL-CHORDS
RHYTHMIC-NOTE
a pairing of NOTE and RHYTHM-VALUE where RHYTHM-VALUE is 1,2,4,8,16
EVENT
an ALIST grouping NOTE, ON-TIME, OFF-TIME, VELOCITY

Concepts

Notes

A note is technically a frequency like “A 440HZ”. But in this program a note is represented as an ALIST with following attributes:

TYPE
The type of the object (NOTE)
NAME
The absolute name of the note on the keyboard
VALUE
The MIDI Value
OCTAVE
The keyboard octave
SOLFEGE
The solfege syllable assigned to the note
(make-note 'A4 69 nil)
((TYPE . NOTE) (NAME . A4) (VALUE . 69) (SOLFEGE) (OCTAVE . 4))

Scale templates

Scale templates are represented by a list of pairs (X . Y) where X is either W or H, representing 1 semitone or 2 semitones, respectivley and Y is a solfege syllable (ie. DO). Y may also be a LIST of enharmonic solfege syllables such as ‘(DI RA) or ‘(FI SE).

W
represents 1 semitone
H
represents 2 semitones

This scale template is used to “realize” scales, stepping and collecting notes according to the semitone pattern (ie W W H W W W H) and assigning a solfege syllable (or list of enharmonic solfa) to the note.

Usage

Making a scale template is done like so:

(make-scale-template '(w w h w w w h)
		       '(do re mi fa so la ti do) )
((W . DO) (W . RE) (H . MI) (W . FA) (W . SO) (W . LA) (H . TI))

Some scale templates are defined as functions:

  • Chromatic scale template uses enharmonic solfa:
    (chromatic-scale-template)
        
    ((H . DO) (H DI RA) (H . RE) (H RI ME) (H . MI) (H . FA) (H FI SE) (H . SO) (H SI LE) (H . LA) (H LI TE) (H . TI))
        
  • Minor scale template
    (minor-scale-template)
        
    ((W . DO) (H . RE) (W . ME) (W . FA) (H . SO) (W . LE) (W . TE))
        
  • Dorian scale template
    (dorian-scale-template)
        
    ((W . DO) (H . RE) (W . ME) (W . FA) (W . SO) (H . LA) (W . TI))
        

Scales

Scales are represented as ALISTS containing LIST of NOTES and a SCALE-TEMPLATE.

NOTES
A LIST of NOTES
TEMPLATE
The scale template used to realize the notes; defaults to the major-scale-template

Usage

#’make-scale

The #’make-scale function is used to create scales from a template. The default scale template is the major scale.

(make-scale 'c4)

(make-scale 'c4 (minor-scale-template))

#’scale-notes

Returns a list of all scale notes.

(take 12 (scale-notes (make-scale 'c4)))
(TYPE . NOTE)(NAME . A0)(VALUE . 21)(SOLFEGE . LA)(OCTAVE . 0)
(TYPE . NOTE)(NAME . B0)(VALUE . 23)(SOLFEGE . TI)(OCTAVE . 0)
(TYPE . NOTE)(NAME . C0)(VALUE . 24)(SOLFEGE . DO)(OCTAVE . 0)
(TYPE . NOTE)(NAME . D0)(VALUE . 26)(SOLFEGE . RE)(OCTAVE . 0)
(TYPE . NOTE)(NAME . E0)(VALUE . 28)(SOLFEGE . MI)(OCTAVE . 0)
(TYPE . NOTE)(NAME . F0)(VALUE . 29)(SOLFEGE . FA)(OCTAVE . 0)
(TYPE . NOTE)(NAME . G0)(VALUE . 31)(SOLFEGE . SO)(OCTAVE . 0)
(TYPE . NOTE)(NAME . A1)(VALUE . 33)(SOLFEGE . LA)(OCTAVE . 1)
(TYPE . NOTE)(NAME . B1)(VALUE . 35)(SOLFEGE . TI)(OCTAVE . 1)
(TYPE . NOTE)(NAME . C1)(VALUE . 36)(SOLFEGE . DO)(OCTAVE . 1)
(TYPE . NOTE)(NAME . D1)(VALUE . 38)(SOLFEGE . RE)(OCTAVE . 1)
(TYPE . NOTE)(NAME . E1)(VALUE . 40)(SOLFEGE . MI)(OCTAVE . 1)

#’scale-range

Returns a new scale object containing a subset of notes

(scale-notes (scale-range 'c3 'c4  (make-scale 'c4)))
(TYPE . NOTE)(NAME . C3)(VALUE . 60)(SOLFEGE . DO)(OCTAVE . 3)
(TYPE . NOTE)(NAME . D3)(VALUE . 62)(SOLFEGE . RE)(OCTAVE . 3)
(TYPE . NOTE)(NAME . E3)(VALUE . 64)(SOLFEGE . MI)(OCTAVE . 3)
(TYPE . NOTE)(NAME . F3)(VALUE . 65)(SOLFEGE . FA)(OCTAVE . 3)
(TYPE . NOTE)(NAME . G3)(VALUE . 67)(SOLFEGE . SO)(OCTAVE . 3)
(TYPE . NOTE)(NAME . A4)(VALUE . 69)(SOLFEGE . LA)(OCTAVE . 4)
(TYPE . NOTE)(NAME . B4)(VALUE . 71)(SOLFEGE . TI)(OCTAVE . 4)
(TYPE . NOTE)(NAME . C4)(VALUE . 72)(SOLFEGE . DO)(OCTAVE . 4)

#’note-range

Returns a subset of notes according to a specified range

(note-range 'c3 'c4  (scale-notes (make-scale 'c4)))
(TYPE . NOTE)(NAME . C3)(VALUE . 60)(SOLFEGE . DO)(OCTAVE . 3)
(TYPE . NOTE)(NAME . D3)(VALUE . 62)(SOLFEGE . RE)(OCTAVE . 3)
(TYPE . NOTE)(NAME . E3)(VALUE . 64)(SOLFEGE . MI)(OCTAVE . 3)
(TYPE . NOTE)(NAME . F3)(VALUE . 65)(SOLFEGE . FA)(OCTAVE . 3)
(TYPE . NOTE)(NAME . G3)(VALUE . 67)(SOLFEGE . SO)(OCTAVE . 3)
(TYPE . NOTE)(NAME . A4)(VALUE . 69)(SOLFEGE . LA)(OCTAVE . 4)
(TYPE . NOTE)(NAME . B4)(VALUE . 71)(SOLFEGE . TI)(OCTAVE . 4)
(TYPE . NOTE)(NAME . C4)(VALUE . 72)(SOLFEGE . DO)(OCTAVE . 4)

#’scale-octaves

Returns an list of pairs (X . Y) where X is a NOTE and Y is an INTEGER value representing an octave relative to the scale. The idea is that there is absolute octaves and relative octaves.

  • Absolute is what the piano octaves are like “C4” or “A#7”. Absolute isn’t related to a key center.
  • Relative octaves are relative to the key center/scale… meaning that a new octave starts on the tonic note.

#’with-scale macro

Chord

Chord Tones

Scale Chords

Scales and chords are two sides of the same coin.

Scale chords provide an object with access to chords and the scale from which they derived.

Scale
the scale
Chords
the list of chords
Roman Numberal Chords
an ALIST associating a roman numeral with the chord

Data and Functions

The initial and most fundamental data we have is a list of MIDI INTEGERS (21..108)

(midi-integers)

Then there is the #’midi-note-octave list of absolute note names and octave

(midi-note-octave)
(first (midi-note-octave)) ; A0
(last (midi-note-octave))  ; (C7)
(length (midi-note-octave)); 88

The #’midi-notes function turns the MIDI integers and absolute note names into the NOTE data structure

(first (midi-notes)); ((TYPE . NOTE) (NAME . A0) (VALUE . 21) (SOLFEGE) (OCTAVE . 0))
(last (midi-notes)); (((TYPE . NOTE) (NAME . C7) (VALUE . 108) (SOLFEGE) (OCTAVE . 7))) 
(length (midi-notes)); 88

At this point, we have a basic representation of all notes on the keyboard. The next step is to build scales.

Scale templates are used to realize scales from the patterns they define. For example, The major scale uses a pattern of “W W H W W W H” where W is 2 semitones and H is 1 semitone.

The #’make-scale-template function is used to make scale templates.

To define the major scale template, set the pattern and the solfege syllables:

(make-scale-template '(w w h w w w h) '(do re mi fa so la ti do))

To realize the scale, use the #’make-scale-from-template function.

The algorithm looks at all notes available and returns only the notes found according to the scale pattern.

The function signature requires a starting note and end note.

To creates a C major scale from C4 to C5:

(let ((major-scale-template
	  (make-scale-template '(w w h w w w h)
			       '(do re mi fa so la ti do))))
  (make-scale-from-template 'C4 'C5 major-scale-template))

Each item in the list is a NOTE – an ALIST representing SOLFEGENAME, NOTENAME, MIDI-VALUE, and KEYBOARD OCTAVE.

The functions #’note-name, #’note-value, #’note-solfege are used to select note data.

CHORDS

The next logical step would be to build up chords.

The C Major scale notes are C D E F G A B. To make chords, you combine every other note in scale:

The triads in C major are “CEG” “DFA” “EGB” “FAC” “GBD” “ACE” “BDF”.

The seventh chords in C major are “CEGA” “DFAG” “EGBD” “FACE” “GBDF” “ACEG” “BDFA”.

Use the #’chord-builder function to generate a list of chords.

#’chord-builder takes a scale and generates a list of chords up the the 13th (remember, a chord is just a list of notes)

(take 7 (let* ((c-major-scale
	   (make-scale-from-template 'C2 'G4
				     (make-scale-template '(w w h w w w h)
							  '(do re mi fa so la ti do)))))
  (chord-builder c-major-scale)))

Triads and Sevenths

The #’triads and #’sevenths functions take a list of chords and reduce each chord to a specific number of notes, 3 and 4 respectively.

The #’chord-take function takes an integer and list of chords and returns a shortened list.

(car (triads (test-chord-builder))) 
=> (((C2 . 48) . DO) ((E2 . 52) . MI) ((G2. 55) . SO))
(car (sevenths (test-chord-builder)))
=> (((C2 . 48) . DO) ((E2 . 52) . MI) ((G2 . 55) . SO) ((B3 . 59) . TI)) 
(car (chord-take 2 (test-chord-builder)))
=> (((C2 . 48) . DO) ((E2 . 52) . MI))

Upcoming documentation

additional chord functions inversions

(defun inversion-test ()
  (chord-play (car (triads (chord-builder (scale-range 'C3 'G5 (make-scale 'c4))))))

  (chord-play (chord-over-3 (car (triads (chord-builder (scale-range 'C3 'G5 (make-scale 'c4)))))  (make-scale 'c4)))

  (chord-play (chord-over-5 (car (triads (chord-builder (scale-range 'C3 'G5 (make-scale 'c4))))) (make-scale 'c4)))

  (chord-play (car (triads (chord-builder (scale-range 'C4 'G5 (make-scale 'c4))))))

  )

 (mapcar #'chord-play (take 8 (triads (modes2 (make-scale-from-template 'C2 'B5 (major-scale-template))))))

(chord-play (chord-invert (car (chords (scale-range 'c3 'G5 (make-scale 'c4)))) (make-scale 'c4)))

(chord-invert (chord-remove-degree (chord-upper (car (cdr (chords (scale-range 'c3 'G5 (make-scale 'c4)) #'sevenths)))) 5) (make-scale 'c4))

with-scale macro

(with-scale (random-major-scale)
  (play-scale *current-scale*))

(with-scale (random-major-scale)
  (play-tonic-subdominant-dominant  *current-scale*))

(with-scale (random-major-scale)
  (play-tonic *current-scale*)
  (sleep 0.5)
  (play-subdominant *current-scale*)
  (sleep 0.5)
  (play-dominant *current-scale*)
  (sleep 0.5)
  (play-tonic *current-scale*))

(with-scale (random-major-scale)
  (solfege-chord '(DO MI SO) *current-scale*))

(with-scale (random-major-scale)
  (play-tonic-subdominant-dominant *current-scale*))

(with-scale (random-major-scale)
  (chord-builder *current-scale*))

(mapcar #'chord-play (triads (chord-builder (build-scale 'C4 (major-scale-template)))))
(mapcar #'chord-play (subseq (triads (chord-builder (build-scale 'C4 (major-scale-template)))) 16 24))

Chord sequencing

    (with-scale (build-scale 'C4 (major-scale-template))
    (play-chords (sevenths (chord-sequence '(I IV V I)
					      (scale-range 'C2 'G3 *current-scale*)))))

    (with-scale (build-scale 'C4 (major-scale-template))
      (let* ((chord-list (take-octaves 2 (chord-builder (scale-range 'A2 'C7 *current-scale*))))
	      (chords (chord-roman-numerals (triads chord-list)))
	      (chord-sequence '(I VI- II- V III- VI- II- V I)))

	 (play-chords (mapcar (lambda (rn)
				(find-chord rn chords))
			      chord-sequence))))

    (chord-sequence-play
     (chord-sequence-chords
      (chord-sequence
	'((octave . 3) I (octave . 3) VI- (octave . 3)  II- (octave . 2) V (octave . 3) I)
	(chords (make-scale 'C4) #'sevenths))))

    (chords (make-scale 'C4) #'sevenths)

Solfege chords

(with-scale (scale-range 'C4 'G5 (make-scale 'C4))
 (solfege-chord '(Do mi so) *current-scale*)
 (solfege-chord '(re fa la) *current-scale*)
 (solfege-chord '(mi so ti) *current-scale*)
 (arp '(do mi so) *current-scale*)
 (rarp '(do mi so) *current-scale*))

Threading function

   (-> (make-scale-chords (make-scale 'C2))
	 (scale-chord-filter #'chord-type-filter #'ninths)
	 (scale-chord-filter #'chord-filter #'chord-butfifth)
	 (scale-chord-filter #'chord-filter #'chord-droproot)
	 (chord-seq '(II-
		      (octave . 2)
		      V
		      (octave . 3)
		      I
		      (octave . 3)
		      VI-
		      (octave . 3)
		      II-
		      (octave . 2)
		      V
		      (octave . 3)
		      I
		      I
		      ) 3))

	   #'chord-seq-play)

Games

Solfege trainer

Melody Game

Bass Game

Files

Tests

  (in-package :ld-music)

  (defun mapnotes (scale fn)
    (mapcar fn (attr2 scale 'notes)))
  (defun maplis (l fn)
    (mapcar fn l))

  (deftest test-midi-notes ()
    (check
      (= 88 (length (midi-notes)))))

  (deftest test-scale-range ()
    (let ((result  (->(make-scale  'c4)
		      (scale-range3 'c4 'c5))))
      (check
	 (= 8 (-> (attr2 result 'notes) (length)))
	 (= 4 (-> (car (attr2 result 'notes)) (attr2 'octave)))
	 (= 5 (attr2 (car (last (attr2 result 'notes))) 'octave)))))

  (deftest test-chromatic-scale-solfege ()
    (check
      (equal '(LA (LI TE) TI DO (DI RA) RE (RI ME) MI FA (FI SE) SO (SI LE) LA (LI TE) TI DO (DI RA) RE (RI ME) MI FA (FI SE) SO (SI LE) LA (LI TE) TI DO (DI RA) RE (RI ME) MI FA (FI SE) SO (SI LE) LA (LI TE) TI DO (DI RA) RE (RI ME) MI FA (FI SE) SO (SI LE) LA (LI TE) TI DO (DI RA) RE (RI ME) MI FA (FI SE) SO (SI LE) LA (LI TE) TI DO (DI RA) RE (RI ME) MI FA (FI SE) SO (SI LE) LA (LI TE) TI DO (DI RA) RE (RI ME) MI FA (FI SE) SO (SI LE) LA (LI TE) TI DO)
	      (-> (make-scale 'c4 (chromatic-scale-template))
		(mapnotes #'note-solfege)))))

  (deftest test-major-scale-solfege ()
    (check
      (equal
	'(LA TI
	  DO RE MI FA SO LA TI
	  DO RE MI FA SO LA TI
	  DO RE MI FA SO LA TI
	  DO RE MI FA SO LA TI
	  DO RE MI FA SO LA TI
	  DO RE MI FA SO LA TI
	  DO RE MI FA SO LA TI DO)
	(-> (make-scale 'c4 (major-scale-template))
	  (mapnotes #'note-solfege)))))

  (deftest test-minor-scale-solfege ()
    (check
      (equal
	'(TE DO RE ME FA SO LE TE
	  DO RE ME FA SO LE TE
	  DO RE ME FA SO LE TE
	  DO RE ME FA SO LE TE
	  DO RE ME FA SO LE TE
	  DO RE ME FA SO LE TE
	  DO RE ME FA SO LE TE DO)
	(-> (make-scale 'c4 (minor-scale-template))
	  (mapnotes #'note-solfege)))))

  (deftest test-find-solfege ()
    (let ((solfege 'do)
	   (l (list (make-note 'c4 72 'do))))
      (check (equal 'do (note-solfege (find-solfege solfege l))))))

  (deftest test-find-solfege2 ()
    (let* ((notes (attr 'notes (make-scale 'c4)))
	    (found-note (find-solfege2 'do notes 5)))

      ;; verify note found in relative octave 
      (check (= 5 (note-relative-octave found-note)))
      ;; verify note found by solfege
      (check (equal 'do (note-solfege found-note)))))

  (deftest test-find-solfege2-chromatic ()
    (let* ((relative-octave 5)
	    (notes (attr 'notes (make-scale 'c4 (chromatic-scale-template))))
	    (found-note (find-solfege2 'di notes relative-octave))
	    (enharmonic-note (find-solfege2 'ra notes relative-octave)))

      ;; verify note found in octave
      (check (= 5 (note-relative-octave found-note)))
      ;; verify note found by solfege
      (check (equal '(di ra) (note-solfege found-note)))
      ;; verify note found by enharmonic solfege
      (check (equal '(di ra) (note-solfege enharmonic-note)))))

  (deftest test-scale-octave-range ()
    ;; verify scale-octave-range returns notes within range
    (check (equal '(4 5)
	      (-> (make-scale 'c4)
		(lambda (scale)
		  (scale-octave-range '4 '5 (attr 'notes scale)))
		(lambda (notes)
		  (find-all-if (lambda (n) (equal 'do (note-solfege n))) notes))
		(maplis #'note-relative-octave)))))

  ;;; TEST NOTE RESOLUTIONS
  (defun test-resolve-note-helper (note notes)
      (-> (resolve-note
	    note
	    notes)
	 (maplis #'note-solfege)))

  (defun checker-fn (a b)
    (eval `(check (equal (quote ,a) (quote ,b)))))

  (defun check-note-resolutions (note->resolutions &optional (scale (make-scale 'c4)))
    (let ((notes (attr 'notes scale)))
      (let ((result t))
	 (dolist (n->r note->resolutions)
	   (unless (checker-fn
		    (second n->r)
		    (test-resolve-note-helper (find-solfege2 (first n->r) notes) notes))
	     (setf result 'f)))
	 result)))

  (deftest test-resolve-notes-major-scale ()
    ;; verify diatonic notes can resolve to DO
    (check-note-resolutions '((do  (do))
			       (re  (re do))
			       (mi  (mi re do))
			       (fa  (fa mi re do))
			       (so  (so la ti do))
			       (la  (la ti do))
			       (ti  (ti do)))))

  (deftest test-resolve--chromatic-scale ()
    ;; verify chromatic notes can resolve to DO
    (check-note-resolutions '(( DO (do))
			       ( DI ((DI RA) DO))
			       ( RA ((DI RA) DO))
			       ( RE (re do) )
			       ( RI ((ri me) re do) )
			       ( ME ((ri me) re do) )
			       ( MI (mi re do) )
			       ( FA (fa mi re do) )
			       ( FI ((FI SE) FA MI RE DO) )
			       ( SO (so la ti do) )
			       ( SI ((SI LE) LA TI DO) )
			       ( LA (la ti do) )
			       ( LI ((LI TE) TI DO) )
			       ( TE ((LI TE) TI DO) )
			       ( TI (ti do)))
			     (make-scale 'c4 (chromatic-scale-template))))
  ;;;; END TEST NOTE RESOLUTION ;;;;


  ;;;; TEST DIATONIC CHORDS ;;;;
  ; seventh chords
  (deftest test-seventh-chords ()
    (let ((chords   (-> (make-scale 'c4)
		       (scale-range3 'c4 'b6) 
		       (make-scale-chords #'sevenths)
		       (scale-chords))))

      ;; verify 7ths are returned
      (check (equal '((DO MI SO TI)
		       (RE FA LA DO)
		       (MI SO TI RE)
		       (FA LA DO MI)
		       (SO TI RE FA)
		       (LA DO MI SO)
		       (TI RE FA LA)
		       (DO MI SO TI))
		     (mapcar #'chord-solfege chords)))))

  ; triads
  (deftest test-triad-chords ()
    (let ((chords   (->
		       (make-scale 'c4)
		       (scale-range3 'c4 'g5) 
		       (make-scale-chords)
		       (scale-chords)
		       (triads))))

      ;; verify triads are returned
      (check (equal '((DO MI SO)
		       (RE FA LA)
		       (MI SO TI)
		       (FA LA DO)
		       (SO TI RE)
		       (LA DO MI)
		       (TI RE FA)
		       (DO MI SO))
		     (mapcar #'chord-solfege chords)))))

  ;;;; TEST DIATONIC CHORDS ;;;;
  (deftest test-chord-sequence ()
    (let* ((chord-data (make-scale-chords (make-scale 'c4)))
	    (sequence '(I II- III- IV V VI- VII I))
	    (chords (chord-sequence2 sequence chord-data)))
      ;; verify default octave
      (check
	 (= 3 (-> chords
		#'chord-sequence-chords
		#'car
		#'chord-notes
		#'car
		#'note-octave))

	 ;; verify chord sequence stored
	 (equal sequence (chord-sequence-romans chords))

      ;; verify solfege realized from roman numeral pattern
	 (equal '((DO MI SO)
		       (RE FA LA)
		       (MI SO TI)
		       (FA LA DO)
		       (SO TI RE)
		       (LA DO MI)
		       (TI RE FA)
		       (DO MI SO))
		     (mapcar #'chord-solfege (triads (chord-sequence-chords chords)))))))



  ;; (deftest test-find-chord ())
  ;; (deftest test-chord-builder ())
  ;; (deftest test-chord-butroot ())
  ;; (deftest test-chord-butfifth ())
  ;; (deftest test-chord-drop-root ())
  ;; (deftest test-chord-invert-upper ())
  ;; (deftest test-make-scale-chords ())
  ;; (deftest test-chord-invert ())
  ;; (deftest test-chord-roman-numerals ())
  ;; (deftest test-scale-chord-filter ())  
  ;; (deftest test-chord-octave-filter ())

note

  • note representation and functions
    (defun note-name-position (note-name &optional (scale (midi-notes)))
    (defun find-note-in-octave (note notes)
    (defun note-attr (note attr) (cdr (assoc attr note)))
    (defun note-name (note) (note-attr note 'name))
    (defun note-value (note) (note-attr note 'value))
    (defun note-solfege (note) (note-attr note 'solfege))
    (defun note-octave (note) (note-attr note 'octave))
    (defun note-relative-octave (note)
    (defun note-equal-p (x y)
    (defun note-solfege-equalp (note solfege)
    (defun note-idx (note &optional (scale (midi-notes)))
    (defun note-octave-up (note scale)
    (defun note-octave-down (note scale)
    (defun parse-note-octave (note-name)
    (defun find-note (name &optional (scale (midi-notes)))
    (defun make-note (name value solfege)
        

scale

Funcations for making scales and scale templates representation.

(defun make-scale-template (steps solfege)
(defun chromatic-scale-template ()
(defun major-scale-template () (make-scale-template '(w w h w w w h) '(do re mi fa so la ti) ))
(defun minor-scale-template () (make-scale-template '(w h w w h w w) '(do re me fa so le te)))
(defun dorian-scale-template () (make-scale-template '(w h w w w h w) '(do re me fa so la ti)))
(defun phrygian-scale-template () (make-scale-template '(h w w w h w) '(do ra me fa so le te)))
(defun make-scale (scale-root &optional (template (major-scale-template)))
(defun make-scale-from-template (p1 p2 scale-template)
(defun build-scale-up (from-note-pos pattern)
(defun build-scale-down (from-note-pos pattern)
(defun assign-solfege (scale scale-template)
(defun assign-relative-octaves (notes &optional (count 0))
(defun midi-notes-from-scale (midi-notes original-scale scale)
(defun midi-notes-from-scale-down-helper (midi-notes original-scale scale)
(defun build-scale (start-note pattern &optional (notes (midi-notes)))
(defun scale-notes (scale)
(defun note-range (n1 n2 notes)
(defun random-scale (template)
(defun random-scale2 (template &optional range)
(defun random-major-scale () (random-scale (major-scale-template)))
(defun random-major-scale2 () (random-scale2 (major-scale-template)))
(defun random-chromatic-scale () (random-scale2 (chromatic-scale-template )))
(defun scale-range (p1 p2 scale-data)
(defun scale-range3 (scale-data p1 p2)
(defun with-scale-helper (scale my-fn)
(defmacro with-scale (scale &body body)
(defun random-note (scale) (nth (random (length scale)) scale))
(defun random-notes (y scale) (loop for x from 1 to y collect (random-note scale)))
(defun solfege-chord (l scale)
(defun find-solfege (solfege lis)
(defun find-solfege2 (solfege notes &optional (octave 4))
(defun solfege->notes (scale solfege-list &optional (octave 4))
(defun find-prev-do-helper (idx scale)
(defun find-prev-do (note scale)
(defun note-to-do (note scale)
(defun remove-after-do (scale)
(defun scale-octave-range-helper (o1 o2 notes)
(defun scale-octave-range (o1 o2 notes)
(defun scale-octave-range2 (o1 o2 scale)
(defun resolve-down (note scale)
(defun resolve-note (note scale)
;; (defun major-scales ()
;; (defun spell-scale (root)

chord

Chord representation and functions

(defun find-chord2 (octave romand-num chord-data)
(defun find-chord (octave romand-num chord-list scale)
(defun make-chord-tone (note degree)
(defun chord-tone-note (chord-tone) (attr 'note chord-tone))
(defun chord-degree (chord-tone) (attr 'degree chord-tone))
(defun chord-notes (chord) (mapcar #'chord-tone-note chord))
(defun chord-builder (l)
(defun make-chords (start-note &optional (filter-fn #'triads) (template (major-scale-template)))
(defun make-scale-chords (scale &optional (filter-fn #'triads) (template (major-scale-template)))
(defun scale-chords (scale-chord-data) (attr 'chords scale-chord-data))
(defun chord-sequence-romans (chord-sequence) (mapcar #'car chord-sequence))
(defun chord-sequence-chords (chord-sequence) (mapcdr chord-sequence))
(defun chord-solfege (chord)
(defun chord-root (chord)
(defun chord-butroot (chord) (chord-remove-degree chord 1))
(defun chord-butfifth (chord) (chord-remove-degree chord 5))
(defun chord-drop-root (chord scale) 
(defun chord-invert-upper (chord)
(defun chord-tone-degree (chord-tone) (attr 'degree chord-tone))
(defun chord-remove-degree (chord degree)
(defun chord-take (n listofchords)
(defun triads (myl) (chord-take 3 myl))
(defun sevenths (myl) (chord-take 4 myl))
(defun ninths (myl) (chord-take 5 myl))
(defun elevenths (myl) (chord-take 6 myl))
(defun thirteenths (myl) (chord-take 7 myl))
(defun chord-triad (chord)
(defun chord-invert (chord scale)
 (defun chord-over-3 (root-position-chord scale)
(defun chord-over-5 (root-position-chord scale)
(defun major-solfege-chords ()
(defun chord-roman-numerals (chord-list)
(defun chord-sequence2 (chord-sequence chord-data &optional (octave 4))
(defun chord-sequence (chord-sequence chords scale &optional (octave 4))
(defun scale-chord-filter (chord-data fn &rest args)
(defun octave-filter (octave)
(defun chord-filter (fn)
(defun chord-type-filter (fn)
(defun chord-seq (chord-data seq &optional (octave 4))

rhythm

Logic for calculating rhythm durations based on BPM

(defun rhythm-values (r)
(defun rhythm->duration-scaled (r bpm)
(defun beat-length (beat bpm)
(defun rhythm->seconds (r bpm)
(defun measure-beats (measure)
(defun make-measure (&optional (result '()))
(defun make-measures (n)
(defun make-rhythmic-notes (notes rhythm-list)
(defun select-rhythm (notes/rhythms)
(defun select-note (notes/rhythms)
(defun rhythmic-notes->midi-messages (rhythmic-notes bpm)
(defun rhythmic-notes->pm-events (rhythmic-notes bpm &optional (*midi-channel* 0))

event

Functions for representing an EVENT (NOTE ON-TIME OFF-TIME VELOCITY) and for playing an event (via portmidi).

(defun make-event (note on-time off-time velocity)
(defun play-event (event)
(defun play-events (events)

util

  • Utility functions
    (defun fdbug (code)
    (defmacro dbug (code)
    (defun mapcdr (seq) (mapcar #'cdr seq))
    (defun attr2 (alist item) (cdr (assoc item alist)))
    (defun attr (item alist) (cdr (assoc item alist)))
    (defun attr= (value item alist) (setf (cdr (assoc item alist)) value))
    (defun random-element (l) (nth (random (length l)) l))
    (defun take (n l) (subseq l 0 n))
    (defun prepend-tail (lis) (append (last lis) (butlast lis)))
    (defun attrs (item &rest attrlist)
    (defun grow (l1 l2 &optional (idx 0))
    (defun pairup (l1 l2)
    (defun shuffle (sequence &optional (seed (make-random-state t)))
    (defun any? (i l)
    (defun lcontains-p (lx l)
    (defun every-p (lx l)
    (defun find-all-if (pred sequ &rest keyword-args &key &allow-other-keys)
    ;; (defun rotate (scale) (append (cdr scale) (list (car scale))))
    ;; (defun rotate-n (n scale)
    (defun map-idx (s)
    (defun car-eq (item other)
    (defun car-fn (fn args)
    (defun flatten (structure)
    (defun split-seq (pred seq)
        

midi

  • Lower-level midi functions
    (defun my-midi-setup ()
    (defun midi-unload ()
    (defun launch-qsynth ()
    (defun kill-qsynth ()
    (defun setup-midi ()
    (defun pm-reload (midi-device-id)
    (defun ensure-midi ()
    (defun pm-terminate ()
    (defun midi-instruments () '(
    (defun midi-note-octave ()
    (defun midi-integers () (loop for x from 0 to 87 collect (+ 21 x)))
    (defun midi-notes ()
    (defun make-message (status data1 data2)
    (defun make-message* (upper lower data1 data2) ;internal
    (defun program-change (program &optional (channel 1) (stream *midi-out3*))
    (defun panic (&optional (channel 1))
    (defun note-on (value &optional (velocity 80) (channel 0) (stream *midi-out3*))
    (defun note-off (value &optional (channel 0) (stream *midi-out3*))
    (defun notes-on (values &optional (velocity 80) (channel 0) (stream *midi-out3*))
    (defun notes-off (values &optional (channel 0) (stream *midi-out3*))
    (defun note-play (note &optional (velocity 80) (channel 0))
    (defun note-stop (note &optional (channel 0))
    (defun note-play-sleep (note)
    (defun write-midi-file-format-0 (outfile midi-notes)
    (defun write-midi-file-format-1 (outfile midi-notes &optional (bpm 60))
        

play

  • NEEDS CLEANUP
  • functions for playing notes
    (defun play-random (scale) (note-play (car (random-note scale))))
    (defun chord-sequence-play (chord-sequence &optional (sleep 1))
    (defun chord-play (chord &optional (sleep 1))
    (defun play-chords (chords)
    (defmacro c (fn &body body) `(,fn (list ,@(mapcar (lambda (x) `',x) body))))
    (defun play-tonic (scale) (note-play (car scale)))
    (defun play-subdominant (scale) (note-play (nth 3 scale)))
    (defun play-dominant (scale) (note-play (nth 4 scale)))
    (defun tonic-subdominant-dominant2 (scale)
    (defun tonic-subdominant-dominant (scale)
    (defun play-tonic-subdominant-dominant (scale)
    (defun smoke-test ()
    (defun play-tonic (scale) (note-play (car scale)))
    (defun play-subdominant (scale) (note-play (nth 3 scale)))
    (defun play-dominant (scale) (note-play (nth 4 scale)))
    (defun play-tonic-subdominant-dominant (scale)
    (defun play-tonic-subdominant-dominant3 (scale)
        

sequencer

  • logic for generating/writing midi sequences to file via the MIDI library
  • logic for generating midi sequences for portmidi
    (defun schedule (time fn &rest args)
    (defun schedule-note (note &optional (on-time 0) off-time (velocity 80))
    (defun note->midi-message (note time-on time-off &optional (*midi-channel* 0))
    (defun midi-timing-track (bpm &optional (*midi-channel* 9))
    (defun midi-seq-format-1 (rhythmic-notes &optional (bpm 60))
    (defun midi-seq-format-0 (notes)
        

games

  • initial logic/functions to support games
    (defun make-game (name logic-fn)
    (defun update-game-lst (key item game)
    (defun my-play-game (game)
    (defun find-answers (type game)
    (defun find-unique-answers (type game)
    (defun playing-p (game)
    ;; (defun repeat-answers (type game)
    ;; (defun score (game)
    ;; (defun stop-game (game)
    (defun read-guess () (mapcar #'intern (cl-ppcre:split "\\s+" (read-line))))
    ;; (defun solfege-trainer ()
    (defun run-melody-game (game)
    (defun play-melody-game ()
    (defun prompt-guess (answer game current-scale)
    (defun run-chord-trainer (game)
    (defun play-chord-trainer ()
    (defun prompt-chord-guess (answer game scale)
    (defun set-bass-scale ()
    (defun play-bass-game ()
    (defun run-bass-game (game)
    (defun prompt-bass-guess (answer game scale)
        

random

  • NEEDS CLEANUP
  • functions for generating random notes / experimenting with note resolutions / cadences
    (defun random-notes ()
    (defun random-chromatic ()
    (defun sing-do ()
    (defun random-chromatic2 ()
    (defun random-chromatic3 ()
        

package

  • Defines the package and exports

output.lisp

  • Should be taken out of this library.

examples

(defun little-sequence (&optional (division 60))
(defun seq (solfege-melody rhythm &optional (scale (scale-range 'c3 'c5 (make-scale 'c3))) (bpm 60))
(defun play-seq (&rest args)
(defun play-seq2 (events)
(defun row-row-row-your-boat ()
(defun row-row-row-your-boat2 ()

Issues

  • setting slot-value ‘midi:dd/nn/cc/bb doesn’t work out of the box because not exported. had to fork cl-midi library and add exports
  • update local cl-portmidi library to get updates and remove make-message* fns as make-message now exported
  • work on setting up exports in ASD file
  • should every file be in the same namespace?
  • scale templates can be defined as VARS instead of functions
  • consider using chromatic scale by default in scale logic and deducing other scales from that
  • portmidi seems flakey in that sometimes the midi device can’t connect after restarting REPL
    • this seemed to have to do with the connection to the device being held open even after the SLIME repl was quit.
    • should now be solved with corrected termination logic, specifically using:
      (pm:abort-midi *midi-out*)
      (pm:close-midi *midi-out*)
              

Features

Completed

  • [ ] DSL for generating rhythmic melodies
    • Single octave melody
      (solfa
       mi 8 re do re mi mi mi 4
       re 8 re re 4 mi 8 so so 4
       mi 8 re do re mi mi mi 4 re 8 re mi
       re 8 do 1)
              
    • Multiple octave melody w/ BPM setting
      	(solfa
              (:bpm 120)
      	 do 4 do do 8 re mi 4
      	 mi 8 re mi fa so 2 +
      	 do 8 do do - so so so
      	 mi mi mi do do do so 4
      	 fa 8 mi 4 re 8 do 1)
              
  • [ ] create chord progressions using roman numerals
       (let* ((chord-data (make-scale-chords (make-scale 'c4)))
    	   (sequence '((octave . 4)
    		       I
    		       (octave . 3)
    		       VI-
    		       (octave . 4)
    		       II-
    		       (octave . 3)
    		       V
    		       (octave . 4)
    		       I))
    	   )
         (triads
          (chord-sequence-chords
    	(chord-sequence2 sequence chord-data))))
        
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . C3) (VALUE . 60) (SOLFEGE . DO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . E3) (VALUE . 64) (SOLFEGE . MI) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . G3) (VALUE . 67) (SOLFEGE . SO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . A3) (VALUE . 57) (SOLFEGE . LA) (RELATIVE-OCTAVE . 3) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . C3) (VALUE . 60) (SOLFEGE . DO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . E3) (VALUE . 64) (SOLFEGE . MI) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . D3) (VALUE . 62) (SOLFEGE . RE) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . F3) (VALUE . 65) (SOLFEGE . FA) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . A4) (VALUE . 69) (SOLFEGE . LA) (RELATIVE-OCTAVE . 4) (OCTAVE . 4)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . G2) (VALUE . 55) (SOLFEGE . SO) (RELATIVE-OCTAVE . 3) (OCTAVE . 2)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . B3) (VALUE . 59) (SOLFEGE . TI) (RELATIVE-OCTAVE . 3) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . D3) (VALUE . 62) (SOLFEGE . RE) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . C3) (VALUE . 60) (SOLFEGE . DO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . E3) (VALUE . 64) (SOLFEGE . MI) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . G3) (VALUE . 67) (SOLFEGE . SO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 5))
         (let* ((chord-data (make-scale-chords (make-scale 'c4)))
    	     (sequence '(I II- III- IV V VI- VII I))
    	     (chords (chord-sequence2 sequence chord-data)))
    	(triads (mapcdr chords)))
        
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . C3) (VALUE . 60) (SOLFEGE . DO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . E3) (VALUE . 64) (SOLFEGE . MI) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . G3) (VALUE . 67) (SOLFEGE . SO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . D3) (VALUE . 62) (SOLFEGE . RE) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . F3) (VALUE . 65) (SOLFEGE . FA) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . A4) (VALUE . 69) (SOLFEGE . LA) (RELATIVE-OCTAVE . 4) (OCTAVE . 4)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . E3) (VALUE . 64) (SOLFEGE . MI) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . G3) (VALUE . 67) (SOLFEGE . SO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . B4) (VALUE . 71) (SOLFEGE . TI) (RELATIVE-OCTAVE . 4) (OCTAVE . 4)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . F3) (VALUE . 65) (SOLFEGE . FA) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . A4) (VALUE . 69) (SOLFEGE . LA) (RELATIVE-OCTAVE . 4) (OCTAVE . 4)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . C4) (VALUE . 72) (SOLFEGE . DO) (RELATIVE-OCTAVE . 5) (OCTAVE . 4)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . G3) (VALUE . 67) (SOLFEGE . SO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . B4) (VALUE . 71) (SOLFEGE . TI) (RELATIVE-OCTAVE . 4) (OCTAVE . 4)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . D4) (VALUE . 74) (SOLFEGE . RE) (RELATIVE-OCTAVE . 5) (OCTAVE . 4)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . A4) (VALUE . 69) (SOLFEGE . LA) (RELATIVE-OCTAVE . 4) (OCTAVE . 4)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . C4) (VALUE . 72) (SOLFEGE . DO) (RELATIVE-OCTAVE . 5) (OCTAVE . 4)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . E4) (VALUE . 76) (SOLFEGE . MI) (RELATIVE-OCTAVE . 5) (OCTAVE . 4)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . B4) (VALUE . 71) (SOLFEGE . TI) (RELATIVE-OCTAVE . 4) (OCTAVE . 4)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . D4) (VALUE . 74) (SOLFEGE . RE) (RELATIVE-OCTAVE . 5) (OCTAVE . 4)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . F4) (VALUE . 77) (SOLFEGE . FA) (RELATIVE-OCTAVE . 5) (OCTAVE . 4)) (DEGREE . 5))
    ((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . C3) (VALUE . 60) (SOLFEGE . DO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 1))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . E3) (VALUE . 64) (SOLFEGE . MI) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 3))((TYPE . CHORD-TONE) (NOTE (TYPE . NOTE) (NAME . G3) (VALUE . 67) (SOLFEGE . SO) (RELATIVE-OCTAVE . 4) (OCTAVE . 3)) (DEGREE . 5))
  • [X] create melodies using solfege syllables
       (-> (make-scale 'c4)
    	(lambda (scale)
    	  (scale-range 'c3 'c5 scale))
    	(lambda (scale)
    	  (solfege->notes scale '(DO RE MI FA SO LA TI DO))))
        
    (TYPE . NOTE)(NAME . C3)(VALUE . 60)(SOLFEGE . DO)(OCTAVE . 3)
    (TYPE . NOTE)(NAME . D3)(VALUE . 62)(SOLFEGE . RE)(OCTAVE . 3)
    (TYPE . NOTE)(NAME . E3)(VALUE . 64)(SOLFEGE . MI)(OCTAVE . 3)
    (TYPE . NOTE)(NAME . F3)(VALUE . 65)(SOLFEGE . FA)(OCTAVE . 3)
    (TYPE . NOTE)(NAME . G3)(VALUE . 67)(SOLFEGE . SO)(OCTAVE . 3)
    (TYPE . NOTE)(NAME . A4)(VALUE . 69)(SOLFEGE . LA)(OCTAVE . 4)
    (TYPE . NOTE)(NAME . B4)(VALUE . 71)(SOLFEGE . TI)(OCTAVE . 4)
    (TYPE . NOTE)(NAME . C3)(VALUE . 60)(SOLFEGE . DO)(OCTAVE . 3)
  • [X] add rhythm to melodic sequences
       (-> (make-scale 'c4)
    	(lambda (scale)
    	  (scale-range 'c3 'c5 scale))
    	(lambda (scale)
    	  (solfege->notes scale '(DO RE MI FA SO LA TI DO)))
    	(lambda (notes)
    	  (make-rhythmic-notes notes '(4 4 4 4 2 2 8 8))))
        
  • [X] preview sequences in-memory via portmidi
    (let* ((bpm 60)
    	   (events (-> (make-scale 'c4)
    		       (lambda (scale)
    			 (solfege->notes (scale-range 'c3 'c5 scale) '(DO RE MI FA SO LA TI DO)))
    		       (lambda (notes)
    			 (rhythmic-notes->pm-events 
    			  (make-rhythmic-notes notes '(4 4 4 4 2 2 8 8)) bpm)))))
      events)
        
    (NOTE (TYPE . NOTE) (NAME . C3) (VALUE . 60) (SOLFEGE . DO) (OCTAVE . 3))(ON-TIME . 0)(OFF-TIME . 1)(VELOCITY . 80)
    (NOTE (TYPE . NOTE) (NAME . D3) (VALUE . 62) (SOLFEGE . RE) (OCTAVE . 3))(ON-TIME . 1)(OFF-TIME . 2)(VELOCITY . 80)
    (NOTE (TYPE . NOTE) (NAME . E3) (VALUE . 64) (SOLFEGE . MI) (OCTAVE . 3))(ON-TIME . 2)(OFF-TIME . 3)(VELOCITY . 80)
    (NOTE (TYPE . NOTE) (NAME . F3) (VALUE . 65) (SOLFEGE . FA) (OCTAVE . 3))(ON-TIME . 3)(OFF-TIME . 4)(VELOCITY . 80)
    (NOTE (TYPE . NOTE) (NAME . G3) (VALUE . 67) (SOLFEGE . SO) (OCTAVE . 3))(ON-TIME . 4)(OFF-TIME . 6)(VELOCITY . 80)
    (NOTE (TYPE . NOTE) (NAME . A4) (VALUE . 69) (SOLFEGE . LA) (OCTAVE . 4))(ON-TIME . 6)(OFF-TIME . 8)(VELOCITY . 80)
    (NOTE (TYPE . NOTE) (NAME . B4) (VALUE . 71) (SOLFEGE . TI) (OCTAVE . 4))(ON-TIME . 8)(OFF-TIME . 8.5)(VELOCITY . 80)
    (NOTE (TYPE . NOTE) (NAME . C3) (VALUE . 60) (SOLFEGE . DO) (OCTAVE . 3))(ON-TIME . 8.5)(OFF-TIME . 9.0)(VELOCITY . 80)
    • Use the #’play-events function
    (let* ((bpm 60)
    	 (events (-> (make-scale 'c4)
    		     (lambda (scale)
    		       (solfege->notes (scale-range 'c3 'c5 scale) '(DO RE MI FA SO LA TI DO)))
    		     (lambda (notes)
    		       (rhythmic-notes->pm-events (make-rhythmic-notes notes '(4 4 4 4 2 2 8 8)) bpm)))))
      (play-events events))
        
  • [X] write sequences to MIDI file
       (-> (make-scale 'c4)
    	(lambda (scale)
    	  (scale-range 'c3 'c5 scale))
    	(lambda (scale)
    	  (solfege->notes scale '(DO RE MI FA SO LA TI DO)))
    	(lambda (notes)
    	  (make-rhythmic-notes notes '(4 4 4 4 2 2 8 8)))
    	(lambda (rhythmic-notes)
    	  (write-midi-file-format-1 "myoutput.midi" rhythmic-notes)))
        
  • [X] ear training - random rhythmic melodies following cadence
    (play-cadence-melody-sequence (make-random-melody-sequence (make-scale 'c4) 4))
    
        
  • [X] ear training - single note following cadence
    (play-cadence-note-sequence (make-random-note-sequence (make-scale 'c4)))
        
  • [X] ear training - chord progression following cadence
    (play-cadence-progression-sequence (make-random-progression-sequence (make-scale 'c4)))
        
  • [X] ear training game - chord game
    (play-chord-trainer)
        
  • [X] ear training game - bass melody game
    (play-bass-game)
        

Upcoming

  • [ ] ear training game - melody game

Ideas for features.

  • represent secondary dominants
  • sequence chords and melodies
  • represent inverted chords
  • add rhythm to chord sequences
  • support midi instruments
  • add pentatonic ear training exercises

About

License:MIT License


Languages

Language:Common Lisp 100.0%