< Back to IRCAM Forum

Inheritance and make-instance for CHORD-SEQ

Dear friends,

I am developing some code for OM, and I’m struggling to identify a particular problem.

I’ve made a series of objects that inherit from CHORD and NOTE classes, and I wish to do the same for the CHORD-SEQ class. That itself is no issue, however, I’ve noticed that after a patch has been saved and closed, the next time it is opened it reverts to type CHORD inside self, rather than the subclasses based on CHORD I’ve created. I think it is because of the line in do-initialize:

  
collect (let ((chord (mki 'chord   
                                            :Lmidic (list! midic)   
                                            :Lvel   (list! vel)  
                                            :Ldur    (list! dur )  
                                            :Loffset (list! offset)  
                                            :LChan (list! chan)  
                                            )))  
                            (setf (LPort chord) port)  
                            chord)

And in particular (mki 'chord, but I can’t figure out how to pass the object type that actually been specified inside self. Any advice you might have, or if you can set me on the right path, would be great!

Thanks,
Nigel

1 Like

Hi Nigel,

Can you show a litlle bit more of your code ? Where do you set the type of your internal chords ?

—Jean

Hi Jean,

Thanks for your quick reply.

I’ve taken out the (cond) block from do-initialize, as I only intend to pass chord-seq and chord-seq-children to the object, and I use this:

    (setf (inside self)   
	  (mapcar #'(lambda (object) (ObjfromObjs object (mki (type-of object))))  
		  types))

Where ‘types’ takes the value from a slot of my chord-seq-child, and is set as follows:

  
(defmethod do-initialize ((self system-seq)  &key LTypes LMidic LVel Loffset  Ldur  Lonset  LChan  Legato LPort)  
  (let ((types (list! LTypes))  
	(midics (list! LMidic))  
        (vels (list! LVel))  
        (durs (list! LDur))  
        (offsets (list! LOffset))  
        (chans (list! LChan))  
        (ports (list! LPort))  
        (defdelay (if (>= (length LOnset) 2)  
                      (- (car (last LOnset))  
                         (car (last LOnset 2)))  
		      1000))  
        (defstart (or (pop LOnset) 0)))  
…)  
…)

The value of LTypes is set by specialised ObjfromObjs methods for lists and sequence*.

Best,
Nigel

mhh… don’t know what to say. I think I don’t get the whole thing.
of cours ethe code in your first post with (mki 'chord …) will generate chords, so why don’t you change it there ?

I’ve tried it in various ways, but I’m still having no success. When I pass the object-type to (mki 'chord …), I’m still getting empty system-seq boxes when I re-open my patches. This is my do-initialize now:

  
(defmethod do-initialize ((self system-seq)  &key LTypes LMidic LVel Loffset  Ldur  Lonset  LChan  Legato LPort)  
  (let ((types (list! LTypes))  
	(midics (list! LMidic))  
        (vels (list! LVel))  
        (durs (list! LDur))  
        (offsets (list! LOffset))  
        (chans (list! LChan))  
        (ports (list! LPort))  
        (defdelay (if (>= (length LOnset) 2)  
                      (- (car (last LOnset))  
                         (car (last LOnset 2)))  
		      1000))  
        (defstart (or (pop LOnset) 0)))  
      
    (setQValue self 1000 :recursive nil)  
      
      
    (setf (inside self)  
	  (loop while (or midics vels durs offsets ports)  
	     for midic = (or (pop midics) midic)  
	     for i in Types  
	     for vel = (or (pop vels) vel)  
	     for dur = (or (pop durs) dur)  
	     for offset = (or (pop offsets) offset)  
	     for chan = (or (pop chans) chan)  
	     for port = (or (pop ports) port) ; (list 0))  
	     collect (let ((chord (mki (type-of i)   
				       :Lmidic (list! midic)   
				       :Lvel   (list! vel)  
				       :Ldur    (list! dur )  
				       :Loffset (list! offset)  
				       :LChan (list! chan)  
				       )))  
		       (setf (LPort chord) port)  
		       chord) ))  

    (loop for chord in (inside self)  
       for onset = defstart then (or (pop LOnset)  (+ onset defdelay))  
       for outset = (or (first LOnset)  (+ onset defdelay))  
       do  (setf (slot-value chord 'offset)  (round onset))  
	 (InContext self (setf (extent chord) (round (- outset onset))))  
	 )  
      
    (adjust-extent self)  
    (QNormalize self)  
      
    (when (> legato 0)  
      (propagate-tempo self)  
      (normalize-chord self legato)))  
    
  self)

I must be misunderstanding something fundamental - my apologies!

Best,
Nigel

Sorry I can’t be more helpful with this at the moment:
Could you maybe attach a complete, self-contained example illustrating the problem ?

Dear Jean,

Thanks very much for your dedication. Please find attached a patch, and a lisp file to load before looking into the patch.

You will see that when you evaluate the system-seq box, all is well - it adds the special chord classes, and if you check ‘ltypes’, it has also registered the types of object. However, once the workspace has been closed and re-opened, the objects inside are gone, but the types are still registered.

Thanks for your help,
Nigel

example.lisp (14.9 KB)

Hi Nigel,

First I have to tell you that you are stepping in dangerous territories here, but you are probably aware of that.
Hacking this part of OM code (the very core of score objects) which might get you into complicate issues. But let’s try it :slight_smile:

So, I think your problem comes from the fact that the chord-seq object has a specific way of saving/reloading itself, using the regular “slots” values for storage (lmidics, lvels, …) and building the chords inside at initialization. Additional slots (like ytour “Ltypes”) are saved and restored as well, but once after chord-seq has already been loaded: therefore, they are not (yet) here to be considered at initializing the instance.

You are redefiniting the initialize-instance method of your object, copying the one of chord-seq including call-next-method and do-initialize, and adding stuff. This is not very good because now, do-initialize gets called twice (and LTypes is always NIL anyway).

=> I first suggest modify this redefinition of initialize-instance. The slot value will be set anyway (later).

What might be interesting is to rather redefine the Ltype accessors on the model of the othee chord-seq slots, so that the setter it calls do-initialize with the type list:

  
(defmethod (setf ltypes) ((ltypes list) (self chord-seq))  
  (do-initialize self   
               :LPort (LPort self)  
               :LMidic (LMidic self)  
               :LVel (LVel self)  
               :LOnset (LOnset self)  
               :LOffset (LOffset self)  
               :LDur (LDur self)  
               :LChan (LChan self)  
               :Legato (legato self)  
               :Ltypes ltypes))  

The reader accessor would simply be:

  
(defmethod Ltypes ((self system-seq))  
  (loop for chord in (inside self)  
        collect (type-of chord)))  

do-initialized will now be called at setting the type List. You can rework how you deal with the type list in there:

  
[...]  
(setf (inside self)  
          (loop while (or midics vels durs offsets ports)  
	     for midic = (or (pop midics) midic)  
             ;;; for i in Types  
             FOR I = (OR (POP TYPES) TYPES 'CHORD)  
	     for vel = (or (pop vels) vel)  
	     for dur = (or (pop durs) dur)  
	     for offset = (or (pop offsets) offset)  
	     for chan = (or (pop chans) chan)  
	     for port = (or (pop ports) port) ; (list 0))  
             ;; do (print i)  
	     collect (let ((chord (mki  I    ;;; (type-of i)   
				       :Lmidic (list! midic)   
				       :Lvel   (list! vel)  
				       :Ldur    (list! dur )  
				       :Loffset (list! offset)  
				       :LChan (list! chan)  
				       )))  
		       (setf (LPort chord) port)  
		       chord) ))  
[...]  

Note that in the previous code I consider types being chord types (symbols) not chord-instance.
Using instances and type-of to determine the type seems not necessary.
You should change as well the default value for ltypes in the system-seq definition accordingly.

Hope this is more or less clear…

Jean

1 Like

Hi Nigel,

First I have to tell you that you are stepping in dangerous territories here, but you are probably aware of that.
Hacking this part of OM code (the very core of score objects) might get you into complicate issues. But let’s try it :slight_smile:

So, I think your problem comes from the fact that the chord-seq object has a specific way of saving/reloading itself, using the regular “slots” values for storage (lmidics, lvels, …) and building the chords inside at initialization (then resetting to NIL). Additional slots (like your “Ltypes”) are saved and restored as well, but after the chord-seq has already been loaded: therefore, they are not (yet) here to be considered at initializing the instance.

You are redefiniting the initialize-instance method of your object, copying the one of chord-seq including call-next-method and do-initialize, and adding stuff. This is not very good because now, do-initialize gets called twice (and LTypes is always NIL anyway).

=> I first suggest you remove this redefinition of initialize-instance. The slot value will be set anyway (later).

What might be interesting is to redefine the Ltype accessors (reader/setter) on the model of the other chord-seq slots, so that the setter calls do-initialize with the Ltype list:

  
(defmethod (setf ltypes) ((ltypes list) (self chord-seq))  
  (do-initialize self   
               :LPort (LPort self)  
               :LMidic (LMidic self)  
               :LVel (LVel self)  
               :LOnset (LOnset self)  
               :LOffset (LOffset self)  
               :LDur (LDur self)  
               :LChan (LChan self)  
               :Legato (legato self)  
               :Ltypes ltypes))  

The reader accessor would simply be:

  
(defmethod Ltypes ((self system-seq))  
  (loop for chord in (inside self)  
        collect (type-of chord)))  

do-initialized will now be called at setting the type List. You can rework how you deal with the Ltypes list in there:

  
[...]  
(setf (inside self)  
          (loop while (or midics vels durs offsets ports)  
	     for midic = (or (pop midics) midic)  
             ;;; for i in Types  
             FOR I = (OR (POP TYPES) TYPES 'CHORD)  
	     for vel = (or (pop vels) vel)  
	     for dur = (or (pop durs) dur)  
	     for offset = (or (pop offsets) offset)  
	     for chan = (or (pop chans) chan)  
	     for port = (or (pop ports) port) ; (list 0))  
             ;; do (print i)  
	     collect (let ((chord (mki  I    ;;; (type-of i)   
				       :Lmidic (list! midic)   
				       :Lvel   (list! vel)  
				       :Ldur    (list! dur )  
				       :Loffset (list! offset)  
				       :LChan (list! chan)  
				       )))  
		       (setf (LPort chord) port)  
		       chord) ))  
[...]  

Note that in the previous code I consider types being chord types (symbols) not chord instances.
Using instances and type-of to determine the type seems not necessary.
You should change as well the default value for ltypes in the system-seq definition accordingly.

Hope this help (and is is more or less clear…)

Jean

Dear Jean,

Thank you so much! You’ve been a tremendous help - my progress can now continue again!

Gratefully,
Nigel