linked-named-object/player [ Classes ]

[ Top ] [ linked-named-object ] [ Classes ]

NAME

 player

 File:             player.lsp

 Class Hierarchy:  named-object -> linked-named-object -> player

 Version:          1.1.0

 Project:          slippery chicken (algorithmic composition)

 Purpose:          Implementation of the player class which holds an
                   instrument or a assoc-list of instruments in it's data
                   slot.    

 Author:           Michael Edwards: m@michael-edwards.org

 Creation date:    7th September 2001

 $$ Last modified:  15:13:59 Fri Aug 23 2024 CEST

 SVN ID: $Id$

player/make-player [ Functions ]

[ Top ] [ player ] [ Functions ]

DESCRIPTION

 Create a player object from a specified instrument-palette object and a
 specified instrument or list of instruments which that player plays. 

 The player object is separate from the instrument object as on player in an
 ensemble may perform more than one instrument ("double"), such as flute and
 piccolo, clarinet and bass clarinet, or sax, flute and clarinet.

ARGUMENTS

 - A symbol which will be the ID of the resulting player object.
 - An instrument-palette object. If NIL then
   +slippery-chicken-standard-instrument-palette+ will be used 
 - A symbol or a list of symbols that are the instruments from the
   specified instrument-palette object that the given player will play, as
   spelled and defined within the instrument-palette object. NB: If only one
   instrument is to be assigned to the given player, it should be stated as
   symbol rather than a list, to avoid errors in the DOUBLES slot.

OPTIONAL ARGUMENTS

 keyword arguments:
 - :midi-channel. An integer that indicates the MIDI channel on which any
   non-microtonal pitch material for this player is to be played
   back. Default = 1.
 - :microtones-midi-channel. An integer that indicates the MIDI channel on
   which any microtonal pitch material for this player is to be played
   back. slippery chicken uses this channel to add MIDI pitch-bends via CM
   so that microtonal chords are possible, but due to a current glitch these
   tracks contain no pitch-bend data. A work-around for this is to simply
   open the MIDI file in a sequencer and shift the entire channel by the
   desired pitch-bend value. Default = -1.
 - :cmn-staff-args. A list of pairs that indicate any additional arguments
   to the call to cmn::staff for this player, such as staff size, number of
   lines etc. Instead of being real cmn function calls, as they would be in
   normal cmn, this is a simple list of pairs; e.g. '(staff-size .8
   staff-lines 3). Defaults = NIL.
 - :staff-names. A symbol, string or list of staff names, generally strings,
   for each instrument the player will play. E.g. '("violin II"). If not
   given, then the instrument's name as defined in the instrument palette
   will be used. Default = NIL.
 - :staff-short-names. A symbol, string or list of short staff
   names. E.g. '("vln2"). Default = NIL

RETURN VALUE

 Returns a player object.

EXAMPLE

;; Create a player object with just one instrument object
(let ((ip (make-instrument-palette 
            'inst-pal 
            '((picc (:transposition-semitones 12 :lowest-written d4
                     :highest-written c6)) 
              (flute (:lowest-written c4 :highest-written d7))  
              (clar (:transposition-semitones -2 :lowest-written e3
                     :highest-written c6))  
              (horn (:transposition f :transposition-semitones -7
                     :lowest-written f2 :highest-written c5))    
              (vln (:lowest-written g3 :highest-written c7 :chords t))  
              (vla (:lowest-written c3 :highest-written f6 :chords t))))))
  (make-player 'player-one ip 'flute))

=> 
PLAYER: (id instrument-palette): INST-PAL 
doubles: NIL, cmn-staff-args: NIL
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: PLAYER-ONE, tag: NIL, 
data: 
INSTRUMENT: lowest-written: 
[...]
NAMED-OBJECT: id: FLUTE, tag: NIL, 
data: NIL

;; Create a player object with two instruments, setting the midi channels using
;; the keyword arguments, then print the corresponding slots to see the changes 
(let* ((ip (make-instrument-palette 
            'inst-pal 
            '((picc (:transposition-semitones 12 :lowest-written d4
                     :highest-written c6)) 
              (flute (:lowest-written c4 :highest-written d7))  
              (clar (:transposition-semitones -2 :lowest-written e3
                     :highest-written c6))  
              (horn (:transposition f :transposition-semitones -7
                     :lowest-written f2 :highest-written c5))    
              (vln (:lowest-written g3 :highest-written c7 :chords t))  
              (vla (:lowest-written c3 :highest-written f6 :chords t))))) 
       (plr (make-player 'player-one ip '(flute picc) 
                         :midi-channel 1
                         :microtones-midi-channel 2)))
  (print (loop for i in (data (data plr)) collect (id i)))
  (print (midi-channel plr))
  (print (microtones-midi-channel plr)))

=>
(FLUTE PICC) 
1 
2

;;; With specified cmn-staff-args
(let ((ip (make-instrument-palette 
           'inst-pal 
           '((picc (:transposition-semitones 12 :lowest-written d4
                    :highest-written c6)) 
             (flute (:lowest-written c4 :highest-written d7))  
             (clar (:transposition-semitones -2 :lowest-written e3
                    :highest-written c6))  
             (horn (:transposition f :transposition-semitones -7
                    :lowest-written f2 :highest-written c5))    
             (vln (:lowest-written g3 :highest-written c7 :chords t))  
             (vla (:lowest-written c3 :highest-written f6 :chords t))))))
  (make-player 'player-one ip '(flute picc) 
               :midi-channel 1
               :microtones-midi-channel 2
               :cmn-staff-args '(staff-size .8 staff-lines 3)))

=> 
PLAYER: (id instrument-palette): INST-PAL 
doubles: T, cmn-staff-args: (#<SELF-ACTING {10097B6E73}>
                             #<SELF-ACTING {10097B6EE3}>), total-notes: 0, total-degrees: 0, 
total-duration: 0.000, total-bars: 0, tessitura: NIL
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: PLAYER-ONE, tag: NIL, 
data: 
[...]

SYNOPSIS

(defun make-player (id instrument-palette instruments
                    &key (cmn-staff-args nil) staff-names staff-short-names
                         (microtones-midi-channel -1) (midi-channel 1))

player/microtonal-chords-p [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Determines whether the MICROTONES-MIDI-CHANNEL slot of the given player
 object is set to a value greater than 0 and is different to the midi-channel
 slot, indicating that the player and its instrument are capable of
 performing microtonal chords.

ARGUMENTS

 - A player object.

RETURN VALUE

 Returns T if the value stored in the MICROTONES-MIDI-CHANNEL slot of the
 given player object is greater than 0, otherwise returns NIL.

EXAMPLE

;; Returns T
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'vln ip 'violin :microtones-midi-channel 2)))
  (microtonal-chords-p plr))

=> T

;; Returns NIL
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'pno ip 'piano)))
  (microtonal-chords-p plr))

=> NIL

SYNOPSIS

(defmethod microtonal-chords-p ((p player))
  ;; MDE Thu Dec 28 17:49:36 2017 -- updated as now make-player sets microtones
  ;; channel to midi-channel if it's not explicitly set
  (and (integer>0 (microtones-midi-channel p))
       (/= (microtones-midi-channel p)
           (midi-channel p))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmethod score-write-bar-line ((p player))
  (let* ((data (data p))
         (ins (if (typep data 'assoc-list)
                  (first (data data))
                data)))
    (score-write-bar-line ins)))

player/player-get-instrument [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Get the instrument object assigned to a single-instrument player object or
 get the specified instrument object assigned to a multiple-instrument
 player object.  

 NB: This method will drop into the debugger with an error if no optional
     argument is supplied when applying the method to a multiple-instrument
     player object. It will also print a warning when supplying an optional
     argument to a player object that contains only one instrument object.  

ARGUMENTS

 - A player object.

OPTIONAL ARGUMENTS

 - Actually a required object for multiple-instrument player objects: The
   symbol that is the ID of the sought-after instrument object, as it
   appears in the instrument-palette with which the player object which
   made. If the given player object consists of only one instrument object,
   this argument is disregarded and a warning is printed. If this argument
   is simply T then the first instrument is returned (in the assoc-list, not
   the piece, of which we have no knowledge).

RETURN VALUE

 Returns an instrument object.

EXAMPLE

;; Returns an instrument object. Needs no optional argument when applied to a
;; player object that contains only one instrument object
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'pno ip 'piano)))
   (player-get-instrument plr))

=>
INSTRUMENT:
[...]
NAMED-OBJECT: id: PIANO, tag: NIL, 
data: NIL

;; Returns the only existing instrument object and prints a warning if using
;; the optional argument when applying to a single-instrument player object 
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'pno ip 'piano)))
  (id (player-get-instrument plr 'piano)))

=> PIANO
WARNING:
   player::player-get-instrument: player PNO has only 1 instrument so optional
argument PIANO is being ignored 

;; Asking for a non-existent instrument obect from a single-instrument player
;; object returns the only existing instrument object instead
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'pno ip 'piano)))
  (id (player-get-instrument plr 'marimba)))

=> PIANO
WARNING:
   player::player-get-instrument: player PNO has only 1 instrument so optional
argument PIANO is being ignored 

;; The ID desired instrument object must be specified when applying the method
;; to a multiple-instrument player object
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'percussion ip '(marimba vibraphone))))
  (id (player-get-instrument plr 'marimba)))

=> MARIMBA

;; Interrupts and drops into the debugger when the optional argument is omitted
;; in applying the method to a multiple-instrument player object
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'percussion ip '(marimba vibraphone))))
   (player-get-instrument plr))

=>
player::player-get-instrument: PERCUSSION doubles so you need to pass the ID of
the instrument you want. 
   [Condition of type SIMPLE-ERROR]

SYNOPSIS

(defmethod player-get-instrument ((p player) &optional ins (warn t))

player/plays-transposing-instrument [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Determine whether a given player object has one or more transposing
 instrument objects assigned to it.

ARGUMENTS

 - A player object.

OPTIONAL ARGUMENTS

 - T or NIL to indicate whether instruments that transpose at the octave are
   to be considered transposing instruments.  T = instruments that transpose
   at the octave are not considered transposing instruments. Default = T.

RETURN VALUE

 Returns T if one or more of the instrument objects assigned to the given
 player object has a transposition value other than C or a
 transposition-semitones value other than 0.

EXAMPLE

;; Create a player object using the 'b-flat-clarinet instrument object
;; definition from the default +slippery-chicken-standard-instrument-palette+,
;; then apply the method. 
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'cl ip 'b-flat-clarinet)))
  (plays-transposing-instrument plr))

=> T

;; Create a player object using the 'flute instrument object definition from
;; the default +slippery-chicken-standard-instrument-palette+, then apply the
;; method. 
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'fl ip 'flute)))
  (plays-transposing-instrument plr))

=> NIL

;; Although the intended procedure is to list single instruments as once-off
;; symbols (as in the previous example), single instruments can also be added
;; as a one-item list
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'fl ip '(flute))))
  (doubles plr))

=> NIL

;; Create a player object using a list that consists of the 'flute and
;; 'alto-sax instrument object definitions from the default
;; +slippery-chicken-standard-instrument-palette+, then apply the method to see
;; that it returns T even when only one of the instruments is transposing.
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'fl ip '(flute alto-sax))))
  (plays-transposing-instrument plr))

=> T

;; Setting the optional argument to NIL causes instruments that transpose at
;; the octave to return T.
(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'db ip 'double-bass)))
  (plays-transposing-instrument plr))

=> NIL

(let* ((ip +slippery-chicken-standard-instrument-palette+)
       (plr (make-player 'db ip 'double-bass)))
  (plays-transposing-instrument plr nil))

=> T

SYNOPSIS

(defmethod plays-transposing-instrument ((p player) 
                                         &optional (ignore-octaves t) ignore)

player/reset-instrument-stats [ Methods ]

[ Top ] [ player ] [ Methods ]

DATE

 23rd August 2013

DESCRIPTION

 Reset the statistics slots for each instrument the player plays.

ARGUMENTS

 - The player object

OPTIONAL ARGUMENTS

 - just-total-duration. If NIL update all statistics slots, otherwise just
   the total-duration slot of each instrument 

RETURN VALUE

 T if the player has instruments, NIL if not.

SYNOPSIS

(defmethod reset-instrument-stats ((p player) &optional just-total-duration)

player/tessitura-degree [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Return a number that represents the average pitch for a specified
 instrument over the course of a piece. The number returned will be degrees
 in the current scale.

ARGUMENTS

 - A player object.

RETURN VALUE

 A number that is the tessitura-degree; i.e., average pitch of the given
 instrument for the entirety of the given musical data.

EXAMPLE

(let ((mini
       (make-slippery-chicken
        '+mini+
        :ensemble '(((vn (violin :midi-channel 1))
                     (va (violin :midi-channel 2))
                     (vc (cello :midi-channel 3))))
        :set-palette '((1 ((gs3 as3 b3 cs4 ds4 e4 fs4 gs4 as4 b4 cs5))))
        :set-map '((1 (1 1 1 1 1)))
        :rthm-seq-palette '((1 ((((2 4) q (e) s (32) 32))
                                :pitch-seq-palette ((1 2 3)))))
        :rthm-seq-map '((1 ((vn (1 1 1 1 1))
                            (va (1 1 1 1 1))
                            (vc (1 1 1 1 1))))))))
  (tessitura-degree (get-data 'vc (ensemble mini))))

=> 136

SYNOPSIS

(defmethod tessitura-degree ((p player))

player/tessitura-note [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Return the value of the TESSITURA-DEGREE slot of a specified player object
 as a note-name symbol.

ARGUMENTS

 - A player object.

RETURN VALUE

 - A note-name symbol.

EXAMPLE

(in-scale :chromatic)

(let ((mini
       (make-slippery-chicken
        '+mini+
        :ensemble '(((vn (violin :midi-channel 1))
                     (va (violin :midi-channel 2))
                     (vc (cello :midi-channel 3))))
        :set-palette '((1 ((gs3 as3 b3 cs4 ds4 e4 fs4 gs4 as4 b4 cs5))))
        :set-map '((1 (1 1 1 1 1)))
        :rthm-seq-palette '((1 ((((2 4) q (e) s (32) 32))
                                :pitch-seq-palette ((1 2 3)))))
        :rthm-seq-map '((1 ((vn (1 1 1 1 1))
                            (va (1 1 1 1 1))
                            (vc (1 1 1 1 1))))))))
  (tessitura-note (first (data (ensemble mini)))))

=> BF3

SYNOPSIS

(defmethod tessitura-note ((p player))

player/total-bars [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Return the number of bars in a specified player object.

ARGUMENTS

 - A player object.

RETURN VALUE

 - An integer.

EXAMPLE

(let ((mini
       (make-slippery-chicken
        '+mini+
        :ensemble '(((vn (violin :midi-channel 1))
                     (va (violin :midi-channel 2))
                     (vc (cello :midi-channel 3))))
        :set-palette '((1 ((gs3 as3 b3 cs4 ds4 e4 fs4 gs4 as4 b4 cs5))))
        :set-map '((1 (1 1 1 1 1)))
        :rthm-seq-palette '((1 ((((2 4) q (e) s (32) 32))
                                :pitch-seq-palette ((1 2 3)))))
        :rthm-seq-map '((1 ((vn (1 1 1 1 1))
                            (va (1 1 1 1 1))
                            (vc (1 1 1 1 1))))))))
  (total-bars (first (data (ensemble mini)))))

=> 5

SYNOPSIS

(defmethod total-bars ((p player))

player/total-degrees [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Return a number that reflects the mean note (tessitura) of a player's
 part. This is calculated by incrementing the TOTAL-DEGREES slot of the
 corresponding instrument object for each attacked note in the player's part
 by the degree of that note (in the scale of the piece), and then dividing
 the sum by the total number of notes in the player's part.

ARGUMENTS

 - A player object.

RETURN VALUE

 - An integer.

EXAMPLE

(in-scale :chromatic)

(let ((mini
       (make-slippery-chicken
        '+mini+
        :ensemble '(((vn (violin :midi-channel 1))
                     (va (violin :midi-channel 2))
                     (vc (cello :midi-channel 3))))
        :set-palette '((1 ((gs3 as3 b3 cs4 ds4 e4 fs4 gs4 as4 b4 cs5))))
        :set-map '((1 (1 1 1 1 1)))
        :rthm-seq-palette '((1 ((((2 4) q (e) s (32) 32))
                                :pitch-seq-palette ((1 2 3)))))
        :rthm-seq-map '((1 ((vn (1 1 1 1 1))
                            (va (1 1 1 1 1))
                            (vc (1 1 1 1 1))))))))
  (total-degrees (first (data (ensemble mini)))))

=> 865

SYNOPSIS

(defmethod total-degrees ((p player))

player/total-duration [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Get the total duration of played notes for a given player over the span of
 a piece.

ARGUMENTS

 - A player object.

RETURN VALUE

 A number that is the total duration in seconds of played notes.

EXAMPLE

(let ((mini
       (make-slippery-chicken
        '+mini+
        :ensemble '(((vn (violin :midi-channel 1))
                     (va (violin :midi-channel 2))
                     (vc (cello :midi-channel 3))))
        :set-palette '((1 ((gs3 as3 b3 cs4 ds4 e4 fs4 gs4 as4 b4 cs5))))
        :set-map '((1 (1 1 1 1 1)))
        :rthm-seq-palette '((1 ((((2 4) q (e) s (32) 32))
                                :pitch-seq-palette ((1 2 3))))
                            (2 ((((2 4) (q) e (s) 32 32))
                                :pitch-seq-palette ((1 2 3)))))
        :rthm-seq-map '((1 ((vn (1 1 1 1 1))
                            (va (2 2 2 2 2))
                            (vc (1 2 1 2 1))))))))
  (print (total-duration (get-data 'vn (ensemble mini))))
  (print (total-duration (get-data 'va (ensemble mini))))
  (print (total-duration (get-data 'vc (ensemble mini)))))

=>
6.875 
3.75 
5.625

SYNOPSIS

(defmethod total-duration ((p player))

player/total-notes [ Methods ]

[ Top ] [ player ] [ Methods ]

DESCRIPTION

 Get the total number of notes (actually events) played by a specified
 player (not rests or tied notes, but midi-notes) in the piece which this
 instrument plays. A chord counts as 1 note/event.

ARGUMENTS

 - A player object.

RETURN VALUE

 - An integer that is the number of notes for that player.

EXAMPLE

(let ((mini
       (make-slippery-chicken
        '+mini+
        :ensemble '(((vn (violin :midi-channel 1))
                     (va (violin :midi-channel 2))
                     (vc (cello :midi-channel 3))))
        :set-palette '((1 ((gs3 as3 b3 cs4 ds4 e4 fs4 gs4 as4 b4 cs5))))
        :set-map '((1 (1 1 1 1 1)))
        :rthm-seq-palette '((1 ((((2 4) q (e) s (32) 32))
                                :pitch-seq-palette ((1 2 3)))))
        :rthm-seq-map '((1 ((vn (1 1 1 1 1))
                            (va (1 1 1 1 1))
                            (vc (1 1 1 1 1))))))))
  (print (total-notes (get-data 'vc (ensemble mini)))))

=> 15

SYNOPSIS

(defmethod total-notes ((p player))