< Back to IRCAM Forum

'Screamer' functions as visual boxes

Hi everyone!

I’m new to the forums so I thought I’d share the results of my efforts to make functions from the constraints library ‘Screamer’ available as visual boxes.

The library:
https://github.com/omggelato/paradigm/releases/download/v.1.0.0/myextension.zip

I also added a modulo operator, absolute value, and a few other basic functions. Note that these OpenMusic boxes are defined within the :OM package.

I’m currently using this library in the course of designing a workspace that generates a second voice for a given theme in 1st, 2nd, 3rd, 4th-species counterpoint. As I continue to add documentation and improve the various patches and functions the workspace could become a useful instructional technology.

The library + workspace:
https://github.com/omggelato/paradigm/releases/download/v.1.0.1/Paradigm__v.1.0.1.zip

One major limitation with implementing Screamer using visual boxes is its reliance on a code-walker that rewrites functions in order to facilitate backtracking. Invoked from within the visual environment, Screamer sees calls to the various OM visual box methods instead of the names of the functions native to Screamer and then raises an error. The functions that invoke Screamer’s solver, ONE-VALUE, ALL-VALUES, etc, are invoked in this library with FIND-ANY and FIND-ALL.

Let me know if you are also working with Screamer too, or if you notice any problems in the library.

Wow, this sounds great. Will definitely have a look. Thanks!

Hi Shantus,
Thanks for sharing your work. I’ll take a look.
I’m also working in a library using Screamer inside OM, with the kind help of Karim Haddad. It’s called OM-Screamer, but it’s on it’s early stages of development.

The library is divided in three parts:

  • The restoration of OM-Backtrack (from OM 4);
  • Screamer-solver (a generic constraint solver);
  • Screamer-score (a constraint solver for generating music scores).
    Feel free to share your comments, if you find this library useful. It still needs a full documentation of the screamer-solver and screamer-score functions, but there are some examples in the tutorials patches.
    Best,
    Paulo

I’m sure that you both have gone further down this road than I. Here some things I’ve noted along the way:

For the constraints-variables boxes instead of the function name ending with ‘-v’ I’ve found that adding a question mark makes it easier to visually distinguish. +v, /v, etc is presented as ?+,?/, etc.

Working with the forwards constraints variables I’ve had some success generating them applying rules and sending them to a solver (I’ve designated it FIND-ALL, but such naming might be too similar to a certain commercial Knowledge-Based System). Evaluating boxes in patches results in calls to functions and methods related to the OM visual boxes system. This confuses Screamer (as you have likely found out) and makes it impossible to use ‘either’ and other functions termed ‘non-deterministic’ within Screamer. With its reliance on a code-walker, certain optimizations it tries to perform don’t immediately seem possible from the visual environment. If you connect screamer:=v to screamer:assert! in a patch, it will work, but Screamer will not call the optimized assert-=v function because as you know the text of the code being processed includes omng box-calls. The same is true for the other optimized functions. I see that your extension uses quoting in calls involving ‘nondeterministic’ functions and forward constraints variables.

My library also tries to cache constraints variables. For instance, if you have a list of variables like: (55 [x1] [x2] [x1] [x4]), and apply some operator to them (such as modulo 2):

(mapcar #'(lambda (x) (?% x 2))

… the second time the ?% operator encounters the variable [x1] it will return the variable it generated the first time. That way, additional constraints applied elsewhere will be applied to the same variable.

The

I’m not sure if this is necessary from a Screamer-architecture point-of-view, and I haven’t run into any situation where it seemed necessary to do this.

I modified TEMPLATE to facilitate working with sequences of variables. If a symbol is prefixed with ? and ends with a ‘+’, a sequence number is incremented. A symbol ending with a ‘#’ maintains the previous sequence number. I find this useful for working with variables corresponding to sequences (like MIDI-note values). So for instance to generate a fourth-species counterpoint for a given theme I can enter:

((nil ?x+) (?x# ?x+) (?x# ?x+) ?x+)
 (62       69        64        62))

which is translated by my om::?template function into:

((nil ?x0) (?x0 ?x1) (?x1 ?x2) ?x3)
 (62       69        64        62))

and then a mixture of variables and symbols when passed to SCREAMER:TEMPLATE (((nil [?x0]) ([?x0] [?x1]) ([?x1] [?x2]) [?x3]) (62 69 64 62))

There are two notes in voice 2 for every note of the C.F. The same variable carries over in voice 2, creating suspensions. A that point, a simple rule can be defined that a dissonant vertical interval be escaped by step.

The functions defined in general.lisp under the heading ‘variables registry’ I don’t think are necessary. Every call to a boolean constraint operator caches the resulting variable if it is not already present in the registry. The combination of the function invoked and its arguments are used as the key in the map. Functions like max, min, and, or, =, /=, etc use set-equality to compare arguments. member-of functions use set-equality to compare the second argument. +, *, avg, etc compare the arguments as unordered lists. The rest of the functions use list-equality. I now leave this feature turned off (t2l::disable-variable-cache-map). If I got rid of it, the library would be substantially smaller.

https://github.com/omggelato/paradigm/releases/download/v.1.1.1/OM.Workspace.and.Library.zip

My interest in finding out more about existing constraints libraries like OM-Screamer or continuing to try to develop this in connection to a visual workspace is their potential as instructional technology. Being able to read through and evaluate several hundred generated counterpoints for a given theme, or to interact with program objects and symbols to design one’s own rule-set for polyphony, can help to level the playing field in music theory classrooms. As for creative applications, I am not sure I can make any promises.

Anyway, OM-Screamer looks very promising, especially the visual score-object. I wasn’t aware of the features in OM 4 being brought into this package. Thanks for sharing!

Hi Shantus,
about Screamer libraries I’ve found the OM-Backtrack (that’s included in OM-Screamer, ported from OM 4), a repository in Github ( https://github.com/music-custom/music-custom/tree/master/lib ) and an old library from PWGL called smc ( https://github.com/JulienVincenot/PWGL-community-library/tree/main/User-library/smc-0.2.7 ), but the latter is very dependant on the PWGL environment. In the OM-Backtrack library there is a mechanism to compile the om patch using screamer::defun instead of cl::defun, and the screamer macros all-values, one-value and print-values is used to generate a non-deterministic patch. You can take a look at the files non-deter-patch.lisp, screamboxes.lisp, screamfuns.lisp and screaminterface.lisp. Maybe these can be helpful.
About the screamer-score, it is possible to create counterpoint too and I’ve made some experiment on this matter. For example, this is a patch that creates an 1st species counterpoint (it’s not perfect yet and I’ve made only for testing purposes):


The screamer-score inputs are:

  • a poly object (in this case we have an open voice - all the notes are the middle C [6000] and only the rhythms will be used by the solver - and the cantus firmus);

  • a screamer-score-domain object (or a list of domains). In this case we have only one domain for the upper voice.

  • the constraints (a lambda patch connected to the constraint-one-voice, constraint-harmony, constraint-profile or constraint-measure) - here are two examples, one for a single voice and another for harmonic intervals:
    constraint-one-voice
    constraint-harmony
    The first constraint calculates the absolute melodic intervals between the notes 1 and 2, 2 and 3, 3 and 4, etc… (That why there are two inputs and the constraint is applied recursively when the mode “n-inputs” is selected). This intervals should be a member of the list in midicents (0 100 200 300 400 500 700 800 900 1200).
    The second constraint is for the harmonic intervals between the two voices and them should be a member of the list (300 400 700 800 900 1200 1500 1600) - only consonantes up to a tenth.
    The result is a poly object and up to now it is working just fine. The advantage of this approach is that the screamer-score creates an screamer variable for each note (a-member-ofv or a-random-member-ofv) and organizes the list of variables for melodic or harmonic constraints (the rests are represented as a null value [nil], like in the Cluster-Engine library).
    I don’t know if this is what are you looking for, but maybe it can help too.
    P.S.: about your library, I did not have time to look at the code yet, but I will try to do it soon.

Best,
Paulo

1 Like

Hi Paulo,

I am going in now to try and see what I can glean from your approach in non-deter-patch. I don’t have the familiarity with the OM-Box classes yet to try to attempt such a feat, but I’ve made a lot of progress using the forward-constraint variables and passing them all to a solver.

Like I pointed out earlier, the species-counterpoint examples I put in the workspace I attached relies on a special nested-list structure to express 1-to-N note-to-note structures. These can be arbitrarily complicated, but in order to define the structure of the list in connection with the forward-chaining rules, backtracking is required. So for instance, with two voices, in 2nd species ((_ _ ) (( ) ( ) ( ))) or 1st species (( _ ) ( _ _)), or some mixture of various templates could then be filled with variables, and then passed through the set of rules. If all of the constraints fail, it could backtrack and define a new list structure where the elements are nested differently.

This sort of list structure allows for most if not all of the patches corresponding to rules to have only one inlet.


An ideal visual control for list-structures that model the 1-to-N relation between voices would be some sort of tree-structure. So for each note in one voice, there might be two corresponding notes in another voice. Very complex nested structures, even. This way, note-to-note polyphony can be represented in a manner that may be conducive to analysis.

There’s a lot of functionality Screamer makes available, even compared to products like KnowledgeWorks.

PS: The first link ‘music-custom’ is an earlier iteration of this workspace I am putting together. I changed my Github login. The name ‘Paradigm’ occurred to me because I thought of strict-polyphony as just one of many possible paradigms. There could perhaps be a new folder corresponding to paradigms like modal-jazz or tonnetz.

PS: I will also check out SMC – it seems as though people have been wrestling with systems of this sort for quite some time.

Hi Shantus,

I’ve looked into your library and workspace and it’s great. Everything works fine.
I’ve found just a little intimidating for a new user and I don’t know how to start creating a new patch and constraints using the library. Maybe a step by step tutorial could help with this.

Today I’ve uploaded a possible new version of OM-Backtrack (version 2.0) that is an expansion of the previous one. It includes a way to use nondeterministic functions visually, inside or outside abstractions. It’s is an very experimental stage, but maybe this can be interesting for you.

Download OM-Backtrack 2.0

The new OM methods one-value, all-values, print-values, etc. doest not rely on evaluating all inputs (with omng-box-value), but generate the code from the boxes and evaluate it. With this is possible to avoid the famous screamer-error of nondeterministic expressions.

For this I’ve created a new class (screamer-valuation-boxes) and defined a new “omng-box-value” method for it. I’ve only some doubts about the use of the letlist global variable here and maybe it’s not correct.

(defclass screamer-valuation-boxes (OMBoxCall) () 
   (:documentation "Screamer Valuation boxes"))

(defmethod omNG-box-value ((self screamer-valuation-boxes) &optional (numout 0))
   "Eval the output indexed by 'numout' for the box 'self'. In this method we call the generic function reference of 'self'."
   (handler-bind ((error #'(lambda (c)
                             (when *msg-error-label-on*
                               (om-message-dialog (string+ "Error while evaluating the box " (string (name self)) " " 
                                                                               (om-report-condition c)
                                                                               )
                                               :size (om-make-point 300 200))
                               (om-abort)))))
     (cond
      ((equal (allow-lock self) "l") 
       (setf (value self) (list (special-lambda-value self (intern (string (reference self)) :s))))
       (car (value self)))
      ((or ;(equal (allow-lock self) "l") 
           (equal (allow-lock self) "o")  
           (and (equal (allow-lock self) "x") (value self)) 
           (and (equal (allow-lock self) "&") (ev-once-p self))) (call-next-method))
      (t (let* ((theinputs (loop for i in (inputs self) 
	                                 collect (connected? i)))
				(code (loop for box in theinputs
				            collect (if box (gen-code (first box) (second box)) 'nil)))					   					 
	            (qargs (loop for val in code collect (if (or (symbolp val) (omlistp val)) `',val val)))
				(oldletlist *let-list*)
			    (themethod (compute-applicable-methods (fdefinition (reference self)) qargs)) form rep)
	            (if (null themethod)
	                (progn (dialog-message (string+ "no method is defined for inputs in box " (name self)))
	                       (abort))
                    (progn
                     (when (and (EditorFrame (car themethod)) (not (compiled? (car themethod))))
                      (modify-genfun (EditorFrame (car themethod))))
	  				;(setf *let-list* nil) <=== ???
					(setf form (let ((valuation (string (reference self))))
					            (cond ((or (equal valuation "ALL-VALUES") 
									       (equal valuation "ONE-VALUE"))
										  `(,(intern (string (reference self)) :s)
										    (let* ,(reverse *let-list*) ,.qargs)))	
									  ((or (equal valuation "N-VALUES") 
									       (equal valuation "ITH-VALUE")) 
										  `(,(intern (string (reference self)) :s) ,(car qargs)
										    (let* ,(reverse *let-list*) ,.(cdr qargs))))
									 (t ;"BEST-VALUE"
									  `(let* ,(reverse *let-list*)
									  (,(intern (string (reference self)) :s) ,.qargs)))))) 
			   (setf rep (multiple-value-list (eval form)))
			))
		   (setf *let-list* oldletlist)
           (when (equal (allow-lock self) "&")
             (setf (ev-once-p self) t)
             (setf (value self) rep))
           (when (equal (allow-lock self) "x")
             (setf (value self) rep))
          (progn (setf (value self) rep)
               (nth numout rep))))))
             )

Here is some examples:

Pythagorean Triples from screams.lisp (Screamer repository)

All Interval Series example (using solution). There is also a new lock button (function button), to pass the function “linear-force” as an argument to static-ordering.

OMLispPatch example (the all-values will evaluate the lisp expression from outside the patch)
omlisppatch

Best,
Paulo

Hi,

Thank you for sharing your works. I hope I am not out of topic, but I have a problem in loading OM_Backtrack both in Linux Fedora 32 and in OsX High Sierra. I receive the same error message, I enclose the screenshot.

immagine

Besides I must change the name of the lib folder from OM-Backtrack-2.0 to OM-Backtrack 2.0, that is I must remove the - and replacing ir with a space to be able to load the library. Doing so the names of the folder and of the main .lisp file correspond.

I have also searched into the code to verify if I could resolve, but, sorry, I am not so a fine programmer…

Any help is welcome, ciao

fdsdb

Hi Fabio,

Thanks for testing the library.

Yes, indeed we have to change the name of the folder. Thanks for the advice.

About the “order” method, I’ve made a little modification on it now and you can test it again to see if it works.

It is not complicated, the idea is to return the lisp function “>” or “<” from a symbol or string (it’s defined on the screamfuns.lisp file). Here is the code:

(defmethod! order ((symb-fn symbol))
 :initvals '('>)
 :indoc '("symbol or string")
 :doc "Order argument for SCREAMER::REORDER = > (descending) or < (ascending)." 
 :menuins '((0 (('> '>) ('< '<))))
 :icon 486
 (eval `(function ,(if (stringp symb-fn) (read-from-string symb-fn) symb-fn))))

(defmethod! order ((symb-fn string))
 :initvals '(">")
 :indoc '("symbol or string")
 :doc "Order argument for SCREAMER::REORDER = > (descending) or < (ascending)." 
 :menuins '((0 ((">" '>) ("<" '<))))
 :icon 486
 (eval `(function ,(if (stringp symb-fn) (read-from-string symb-fn) symb-fn))))

Unfortunately I don’t have a Linux system to test this modification and I don’t know why this particular error is happening. Maybe Karim can help us with this.

So, please, contact me if it don’t work and I’ll try to find another solution.

P.S.: This is my latest test on Windows 10 - OM 7.4:
order-fn

Best,
Paulo

Dear Paulo,

Thank you for fast reply, unfortunately the library does not load both on Linux and OsX, I have just tested, sorry. The error message is exactly the same.

Ciao

Fabio

Dear Fabio,

Thanks for testing again.

I was searching here on the Forum and apparently this error is because the method “order” is already defined (see here or here).

I’ve made a new modification and defined this method only once, accepting any argument type and signaling an error if it’s not a symbol or string. The :menuin was also removed.

Maybe it works now.

P.S.: I’ve tested now on Mac Mojave - OM 7.4:

Best,
Paulo

Dear Paulo,

Sorry, also now the situation has not changed, same error advice, in Mac and in Linux. Please, don’t waste your time, I was simply curious to observe how your library functioned, in future I want to deepen the constraint programming in OM. In PWGL there was that beautiful library by Baboni-Schilingi that simplified a lot the composing…

Many thanks for your disposability, of course I am to disposal for testing, but, really, without wasting your time.

Ciao

Fabio

Dear Fabio,

It is my first time using Linux, but I’ve tested the library here in Fedora Workstation (VM) and I could not reproduce the error. Everything is working normally, including the patches that uses the “order” method:

Best,
Paulo

Dear Paulo,

Thank you very much for the information. Now I am not at home, so I can only suppose it can depend on some conflict with some other library. I will test your library alone as soon as possible, I will keep you updated.

Obrigado, ciao

Fabio

Dear Paulo.

I have tested your library on my LInux on a new Workspace without any library, and then adding the librariers I usually use, and it seems to me that your library runs good. The problem is probably in my Workspace (almost identical to the one in Mac computer), with some time at disposition I will test why.

Thank you for your work, ciao

Fabio

OK, solved: restoring Workspace preverences did its work, now the library runs.

Dear Fabio,

Many thanks for helping testing the library and sorry about the delay.

The OM-Backtrack 2.0 is only the first step to bring more features of Screamer to visual programming language. It is far for being a library that contains built-in contraints already adapted to solve musical problems, like JBS-Constraints (which was built on top of Mikael Laurson’s PMC-ENGINE and MULTI-PMC), but now it is possible to create screamer variables, constraints and ask for a solution using the new OM methods all-values, one,value, etc. The first version of OM-Backtrack doest not allows the user to do this locally, only globally (by selecting the valuation mode in the preferences menu).

It is now also possible to use the propagation part of screamer (generalized forward-checking) with variables generators (a-member-ofv, an-integer-betweenv, stc.), apply constraints to those variables (with screamer::assert!) and search for a solution (using the valuation methods combined with the new OM method “solution”).

I’ve included some examples like the All Interval Series calculation, some examples from Screamer’s original repository (like the N-Queens puzzle and Pythagorean Triples) and some other tests.

In the future I will try to create more examples of musical CSPs as well.

Best,
Paulo