;------------------------------------------------------------
; File:  melody.l of domains of clay of clos of lisp of ai

;------------------------------------------------------------
; Enter clos
  ( in-package "pcl" )

;------------------------------------------------------------
; Required stuff
  ( load "/home/blue/ai/lisp/clos/blue.l" )

;------------------------------------------------------------
; Modeling a NOTE

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( defclass note
    () 
    ( ( degree :accessor note-degree :initarg :degree :initform 1 ) 
      ( duration :accessor note-duration
                 :initarg :duration 
                 :initform 4
      )
      ( register :accessor note-register :initarg :register :initform 0 )
      ( amplitude :accessor note-amplitude :initarg :amplitude :initform 0.5 )
    )
  )

  ( defmethod display ( (os t) ( n note ) )
    ( format t "deg = ~A; dur = ~A; reg = ~A; amp = ~A"
      ( note-degree n )
      ( note-duration n )
      ( note-register n )
      ( note-amplitude n )
      os
    )
  )


;------------------------------------------------------------
; Modeling a SCALE

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( defclass scale
    () 
    ( ( key :accessor scale-key :initarg :key :initform 'c ) 
      ( mode :accessor scale-mode
                  :initarg :mode
                  :initform *major*
      )
    )
  )

  ( defmethod display ( (os t)( s scale ) )
    ( format t "key = ~A; mode = ~A"
      ( scale-key s )
      ( scale-mode s )
      os
    )
  )

  ( defmethod size ( ( s scale ) )
    ( length ( mode-intervals ( scale-mode s ) ) )
  )

;------------------------------------------------------------
; Processing NOTEs in the context of SCALES

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( defmethod raise-pitch ( ( n note ) ( s scale ) )
    ( setf ( note-degree n ) 
      ( if ( = ( note-degree n ) ( size s ) )
        1
        ( + ( note-degree n ) 1 ) 
      )
    )
    ( if ( = ( note-degree n ) 1 )
      ( setf ( note-register n ) ( + ( note-register n ) 1 ) )
    )
  )

  ( defmethod lower-pitch ( ( n note ) ( s scale ) )
    ( setf ( note-degree n ) 
      ( if ( = ( note-degree n ) 1 )
        ( size s )
        ( - ( note-degree n ) 1 ) 
      )
    )
    ( if ( = ( note-degree n ) ( size s ) )
      ( setf ( note-register n ) ( - ( note-register n ) 1 ) )
    )
  )

;------------------------------------------------------------
; Modeling a MODE

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( defclass mode
    () 
    ( ( name :accessor mode-name :initarg :name :initform 'major ) 
      ( intervals :accessor mode-intervals
                  :initarg :intervals
                  :initform '( 2 2 1 2 2 2 1 )
      )
    )
  )

  ( defmethod display ( (os t) ( m mode ) )
    ( format t "name = ~A; intervals = ~A"
      ( mode-name m )
      ( mode-intervals m )
      os
    )
  )

;------------------------------------------------------------
; CREATE standard MODES

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( defconstant *major* 
    ( make-instance 'mode :name 'major :intervals '( 2 2 1 2 2 2 1 ) )
  )
  ( defconstant *dorian* 
    ( make-instance 'mode :name 'dorian :intervals '( 2 1 2 2 2 1 2 ) )
  )
  ( defconstant *phrygian* 
    ( make-instance 'mode :name 'phrygian :intervals '( 1 2 2 2 1 2 2 ) )
  )
  ( defconstant *lydian* 
    ( make-instance 'mode :name 'lydian :intervals '( 2 2 2 1 2 2 1 ) )
  )
  ( defconstant *mixolydian* 
    ( make-instance 'mode :name 'mixolydian :intervals '( 2 2 1 2 2 1 2 ) )
  )
  ( defconstant *minor* 
    ( make-instance 'mode :name 'minor :intervals '( 2 1 2 2 1 2 2 ) )
  )
  ( defconstant *locrian* 
    ( make-instance 'mode :name 'locrian :intervals '( 1 2 2 1 2 2 1 ) )
  )
  ( defconstant *wholetone* 
    ( make-instance 'mode :name 'wholetone :intervals '( 2 2 2 2 2 2  ) )
  )
  ( defconstant *pentatonic* 
    ( make-instance 'mode :name 'pentatonic :intervals '( 2 3 2 2 3 ) )
  )
  ( defconstant *ionian* *major* )
  ( defconstant *aeolian* *minor* )

  ( defconstant *modes* 
    ( list *major* *dorian* *phrygian* *lydian*
           *mixolydian* *minor* *locrian* *wholetone* *pentatonic*
           *ionian* *aeolian* 
    )
  )


;------------------------------------------------------------
; the simple commands

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( defmethod |melody>show| ( ( gc green-clay) )
    ( display (clay-output-stream gc) *note* )
  )

  ( defmethod |melody>play| ( ( gc green-clay) &aux os)
    ( setf os (clay-output-stream gc) )
    ( cond
      ( ( < ( note-register *note* ) -4 )
        ( write-line "    PITCH IS TOO LOW" os )
      )
      ( ( > ( note-register *note* ) 4 )
        ( write-line "    PITCH IS TOO HIGH" os )
      )
      ( ( eq *textual-pitch-output-preference* 'functional )
        ( print-functional-pitch os *note* )
        ( if ( eq *textual-duration-output-preference* 'spacial )
          ( print-spacial-duration os *note* )
          ( print-actual-duration os *note* *scale* )
        )
      )
      ( t
        ( print-actual-pitch os *note* *scale* )
        ( if ( eq *textual-duration-output-preference* 'spacial )
          ( print-spacial-duration os *note* )
          ( print-actual-duration os *note* *scale* )
        )
      )
    )
    ( terpri )
  )

  ( defmethod print-functional-pitch ( (os t) ( n note ) )
    ( princ ( note-register-string ( note-register n ) ) os )
    ( princ " " os )
    ( prin1 ( note-degree n ) os )
    ( princ "  " os )
  )

  ( defmethod print-spacial-duration ( (os t) ( n note ) )
    ( princ ( note-duration-string ( note-duration n ) ) os )
  )

  ( defmethod print-actual-pitch ( ( n note ) ( s scale ) &aux key )
    ( princ ( note-register-string ( note-register n ) ) os )
    ( princ " " os )
    ( setf key ( determine-key n s ) )
    ( prin1 key os )
    ( if ( = ( length ( symbol-name key ) ) 1 )
      ( princ " " os )
      ( princ "  " os )
    )
  )

  ( defmethod determine-key ( ( n note ) ( s scale ) 
      &aux d k i hs p x the-key
    )
    ( setf d ( note-degree n ) )
    ( setf k ( scale-key s ) )
    ( setf i ( mode-intervals ( scale-mode s ) ) )
    ( setf hs ( apply #'+ ( subseq i 0 ( - d 1 ) ) ) )
    ( setf p ( position k *physical-key-name-sequence* ) )
    ( setf x ( + p hs ) )
    ( setf the-key ( elt *physical-key-name-sequence* x ) )
    the-key
  )

  ( defmethod print-actual-duration ( (os t) ( n note ) ( s scale ) )
    ( princ ( note-duration n ) os )
  )

  ( defmethod note-register-string ( ( n number ) )
    ( cond
      ( ( = n 0 )
        "    "
      )
      ( ( > n 0 )
        ( concatenate 'string 
          ( subseq ( note-register-string ( - n 1 ) ) 1 ) "H" 
        )
      )
      ( ( < n 0 )
        ( concatenate 'string 
          ( subseq ( note-register-string ( + n 1 ) ) 1 ) "L" 
        )
      )
    )
  )

  ( defmethod note-duration-string ( ( n number ) )
    ( cond
      ( ( = n 0 )
        ""
      )
      ( ( > n 0 )
        ( concatenate 'string 
          ( note-duration-string ( - n 1 ) ) "-" 
        )
      )
      ( ( < n 0 )
        ( error "Negative note duration!" )
      )
    )
  )


  ( defmethod |melody>rest| ( ( gc green-clay) )
    ( princ "       " )
    ( princ ( note-duration-string ( note-duration *note* ) ) )
    ( terpri )
  )

  ( defmethod |melody>rp| ( ( gc green-clay) )
    ( raise-pitch *note* *scale* )
    NIL
  )

  ( defmethod |melody>lp| ( ( gc green-clay) )
    ( lower-pitch *note* *scale* )
    NIL
  )

  ( defmethod |melody>d| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( * 2 ( note-duration *note* ) ) )
    NIL
  )

  ( defmethod |melody>h| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( / ( note-duration *note* ) 2 ) )
    NIL
  )

  ( defmethod |melody>triple| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( * 3 ( note-duration *note* ) ) )
    NIL
  )

  ( defmethod |melody>third| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( / ( note-duration *note* ) 3 ) )
    NIL
  )

  ( defmethod |melody>quadruple| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( * 4 ( note-duration *note* ) ) )
    NIL
  )

  ( defmethod |melody>quarter| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( / ( note-duration *note* ) 4 ) )
    NIL
  )

  ( defmethod |melody>dot| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( * 1.5 ( note-duration *note* ) ) )
    NIL
  )

  ( defmethod |melody>undot| ( ( gc green-clay) )
    ( setf ( note-duration *note* ) ( / ( note-duration *note* ) 1.5 ) )
    NIL
  )

  ( defmethod |melody>abstract| ( ( gc green-clay) )
    ( setf *textual-pitch-output-preference* 'functional )
    ( setf *textual-duration-output-preference* 'spacial )
    NIL
  )

  ( defmethod |melody>actual| ( ( gc green-clay) )
    ( setf *textual-pitch-output-preference* 'actual )
    ( setf *textual-duration-output-preference* 'actual )
    NIL
  )


;------------------------------------------------------------
; Set useful SYSTEM OBJECTS

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( setf *physical-key-name-sequence* 
    '( c cd d de e f fg g ga a ab b c cd d de e f fg g ga a ab b c )
  )


;------------------------------------------------------------
; CREATE the FEATURED OBJECTS

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( setf *note* ( make-instance 'note ) )
  ( setf *scale* ( make-instance 'scale ) )


;------------------------------------------------------------
; Set default SYSTEM PARAMETERS

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( setf *textual-pitch-output-preference* 'functional )
  ( setf *textual-duration-output-preference* 'spacial )


;------------------------------------------------------------
; establish the FAMILY

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  ( setf *domain-family* ( make-instance 'green-clay-command-family ) )
  ( setf ( green-clay-command-family-name *domain-family* ) "melody" ) )
  ( setf ( green-clay-command-family-commands *domain-family* ) '() ) )

  ( setf *new-command* 
    ( make-green-clay-command
      "play" "Render the note sonicly and/or textually and/or graphicly."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "rest" "Play the note silently."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "rp" "Raise the pitch of the note one scale degree."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "lp" "Lower the pitch of the note one scale degree."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "d" "Double the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "h" "Halve the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "triple" "Triple the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "third" "Third the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "quadruple" "Quadruple the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "quarter" "Quarter the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "dot" "Increase by 50% the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf *new-command* 
    ( make-green-clay-command
      "undot" "Decrease by 1/3 the duration of the note."
    )
  )
  ( push 
    *new-command* ( green-clay-command-family-commands *domain-family* ) 
  )

  ( setf 
    ( green-clay-command-family-commands *domain-family* ) 
    ( reverse
      ( green-clay-command-family-commands *domain-family* ) 
    )
  )

  ( order *domain-family* )
