Jump to content
Sign in to follow this  
torstenanders

Updated library of many custom Opusmodus functions

Recommended Posts

Dear all,


I updated my library tot (https://github.com/tanders/tot) in various ways. In particular, there are many new functions available.


You can now read the documentation online at https://tanders.github.io/tot/. However, remember that you can directly evaluate the many examples in the documentation when (after installing the library) you drag the library folder into your Opusmodus project navigator and open the documentation within Opusmodus.

 

If you are interested in the details of how the library developed, you can see a changelog at https://github.com/tanders/tot/commits/master .


NOTE: When you install/upgrade this library, make sure you also install/upgrade all its dependencies, as described in the detailed installation instructions at https://github.com/tanders/tot

 

Best,
Torsten
 

PS: This is not an official release. As I am primarily developing this library for my own purposes, I keep it rather informal and extend it on a continuous basis for my own composition projects. Anyway, I thought at least some of you might be interested to learn that there have been many developments 🙂

Share this post


Link to post
Share on other sites

Thanks, Torsten !

Very nice. I want to try it.

I did the Git terminal stuff

 

I don't  know how to to this part...

ASFD is something I have to install ?

 

???

Best,

Julio

 

USAGE part (???)

 

This library is an ASDF system (ASDF is the de facto standard for building Common Lisp software), and you can load it into your Lisp compiler as follows. All dependency libraries are loaded automatically as well.

(require :tot)

For most convenient use within Opusmodus, you can drag and drop the whole folder of this library in the browser of an Opusmodus project. Then, visit the HTML documentation of this library from within Opusmodus: open the tot, and then its doc folder and click on an HTML file, say index.html. You can now browse the reference documentation of the library, which explains its individual definitions. Most importantly, you can directly run all example code in the documentation from within Opusmodus (thanks, Janusz Podrazik).

added 1 minute later

Second question :

When I update to next Opusmodus release, do I have to reinstall it ?

Best,

Julio

Share this post


Link to post
Share on other sites

Dear Julio,

 

As the link you copied said, "ASDF is the de facto standard build facility for Common Lisp." In other words, it is the standard software for organising, compiling, loading etc. Common Lisp programs that consist of more than just one file. I think it is already built into CCL, the compiler used by Opusmodus. Have you tried after the installation to simply run the given Common Lisp command to load the library?

(require :tot)

>  When I update to next Opusmodus release, do I have to reinstall it ?

 

Not if you follow the installation instructions and install it in ~/common-lisp/

 

Best,

Torsten

 

Share this post


Link to post
Share on other sites

Dear Torsten,

Thank you. Now I understand that this command is done inside Opusmodus.

Unfortunately, I got some errors...

 

;Compiling "/Users/acme/common-lisp/string-tools/make-package.lisp"...
;Compiling "/Users/acme/common-lisp/string-tools/string-tools.lisp"...
;Compiling "/Users/acme/common-lisp/string-tools/export.lisp"...
;Compiling "/Users/acme/common-lisp/ta-utilities/make-package.lisp"...
;Compiling "/Users/acme/common-lisp/ta-utilities/my-utilities.lisp"...
;Compiling "/Users/acme/common-lisp/ta-utilities/export.lisp"...
;Compiling "/Users/acme/common-lisp/fenv/sources/make-package.lisp"...
;Compiling "/Users/acme/common-lisp/fenv/sources/macros.lisp"...
;Compiling "/Users/acme/common-lisp/fenv/sources/fenv.lisp"...
;Compiling "/Users/acme/common-lisp/fenv/sources/export.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/package.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/from-studio-flat.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/01.domain.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/02.engine.lisp"...
;Compiler warnings for "home:common-lisp;cluster-engine;sources;02.engine.lisp.newest" :
;   In poly-engine: type declaration for unknown variable master-index
;   In poly-engine: Undeclared free variable *debug-count-backtrack*
;   In an anonymous lambda form at position 22432: Duplicate definitions of assign-pitches-for-motif, in this file
;   In an anonymous lambda form at position 23073: Duplicate definitions of assign-pitches-for-nth-motif, in this file
;   In fail: Undeclared free variable *debug-count-backtrack* (2 references)
;   In an anonymous lambda form at position 36784: Duplicate definitions of backjump-engine, in this file
; Warning: Lisp compilation failed while compiling #<cl-source-file "cluster-engine" "sources/02.engine">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "cluster-engine" "sources/02.engine">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/03.Fwd-rules.lisp"...
;   In fwd-rule-indep: type declaration for unknown variable length-metric-engin
;   In fwd-rule2: type declaration for unknown variable length-metric-engin
;   In fwd-rule3: type declaration for unknown variable length-metric-engin
;   In fwd-rule4: type declaration for unknown variable length-metric-engin
;   In fwd-rule5: type declaration for unknown variable length-metric-engin
;   In fwd-rule6: type declaration for unknown variable length-metric-engin
;   In fwd-rule6b: type declaration for unknown variable length-metric-engin
; Warning: Lisp compilation failed while compiling #<cl-source-file "cluster-engine" "sources/03.Fwd-rules">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "cluster-engine" "sources/03.Fwd-rules">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/04.Backtrack-rules.lisp"...
;   In backtrack-rule2: type declaration for unknown variable highest-index
; Warning: Lisp compilation failed while compiling #<cl-source-file "cluster-engine" "sources/04.Backtrack-rules">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "cluster-engine" "sources/04.Backtrack-rules">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05.rules-interface.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05a.rules-interface-1engine.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05b.rules-interface-2engines.lisp"...
;Compiler warnings for "home:common-lisp;cluster-engine;sources;05b.rules-interface-2engines.lisp.newest" :
;   In get-start-nth-pointer-durations: type declaration for unknown variable rhythmseq
;   In get-end-nth-pointer-durations: type declaration for unknown variable rhythmseq
; Warning: Lisp compilation failed while compiling #<cl-source-file "cluster-engine" "sources/05b.rules-interface-2engines">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "cluster-engine" "sources/05b.rules-interface-2engines">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05c.rules-interface-2engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05d.rules-interface-2engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05e.rules-interface-2engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05f.rules-interface-3engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05g.rules-interface-any-n-engines.lisp"...
;Compiler warnings for "home:common-lisp;cluster-engine;sources;05g.rules-interface-any-n-engines.lisp.newest" :
;   In get-pitches-for-slices-of-notecounts: type declaration for unknown variable notecount
;   In get-pitches-for-slices-of-notecounts: type declaration for unknown variable voicenr
;   In get-pitches-for-slices-of-notecounts: type declaration for unknown variable notecount-slice
;   In replace-notecount-by-timepoint: type declaration for unknown variable timepoint
;   In replace-notecount-by-timepoint: type declaration for unknown variable notecountgroups
;   In get-durations-for-slices-of-notecounts: type declaration for unknown variable notecount
;   In get-durations-for-slices-of-notecounts: type declaration for unknown variable voicenr
;   In get-durations-for-slices-of-notecounts: type declaration for unknown variable notecount-slice
;   In get-timepoints-for-slices-of-notecounts: type declaration for unknown variable notecount
;   In get-timepoints-for-slices-of-notecounts: type declaration for unknown variable voicenr
;   In get-timepoints-for-slices-of-notecounts: type declaration for unknown variable notecount-slice
; Warning: Lisp compilation failed while compiling #<cl-source-file "cluster-engine" "sources/05g.rules-interface-any-n-engines">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "cluster-engine" "sources/05g.rules-interface-any-n-engines">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05h.rules-higher-level.lisp"...
;Compiler warnings for "home:common-lisp;cluster-engine;sources;05h.rules-higher-level.lisp.newest" :
;   In an anonymous lambda form at position 453: Duplicate definitions of melodic-statement-for-dur, in this file
; Warning: Lisp compilation failed while compiling #<cl-source-file "cluster-engine" "sources/05h.rules-higher-level">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "cluster-engine" "sources/05h.rules-higher-level">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05i.rules-stop-search.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/05n.rules-interface-nn-engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06.heuristic-rules-interface.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06a.heuristic-rules-interface-1engine.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06b.heuristic-rules-interface-2engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06c.heuristic-rules-interface-2engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06d.heuristic-rules-interface-2engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06e.heuristic-rules-interface-2engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06f.heuristic-rules-interface-3engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/06g.heuristic-rules-interface-any-n-engines.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/07.backjumping.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/08.decode.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/09.utilities.lisp"...
;Compiler warnings for "home:common-lisp;cluster-engine;sources;09.utilities.lisp.newest" :
;   In get-nr-for-engine-with-highest-index: type declaration for unknown variable engine
;   In remove-list-before-startpoint: type declaration for unknown variable endpoint
;   In get-durations-at-notecounts: type declaration for unknown variable notecount
;   In get-durations-at-notecounts: type declaration for unknown variable voicenr
;   In get-durations-at-notecounts: type declaration for unknown variable notecounts
;   In get-timepoints-at-notecounts: type declaration for unknown variable notecount
;   In get-timepoints-at-notecounts: type declaration for unknown variable voicenr
;   In get-timepoints-at-notecounts: type declaration for unknown variable notecounts
;   In get-timepoints-at-notecounts-one-voice: type declaration for unknown variable notecount
;   In get-timepoints-at-notecounts-one-voice: type declaration for unknown variable notecounts-all-voice
;   In get-timepoints-at-notecounts-one-voice: type declaration for unknown variable list-voicenrs
;   In remove-rests-and-gracenotes-from-durationlist: type declaration for unknown variable timepoints
;   In get-timepoints-from-any-timepoint-minus-nsteps-ignor-rests-and-gracenotes: type declaration for unknown variable vsolution
;   In get-events-from-any-timepoint-minus-nsteps-ignor-rests-and-gracenotes: type declaration for unknown variable vsolution
;   In get-notecounts-from-any-timepoint-minus-nsteps-ignor-rests-and-gracenotes: type declaration for unknown variable vsolution
;   In get-timepoints-from-any-timepoint-minus-nsteps-ignor-rests: type declaration for unknown variable vsolution
;   In get-events-from-any-timepoint-minus-nsteps-ignor-rests: type declaration for unknown variable vsolution
;   In get-notecounts-from-any-timepoint-minus-nsteps-ignor-rests: type declaration for unknown variable vsolution
;   In get-timepoints-from-any-timepoint-minus-nsteps-ignor-rests-simplify-gracenotes: type declaration for unknown variable vsolution
;   In get-timepoints-from-any-timepoint-minus-nsteps: type declaration for unknown variable vsolution
;   In get-notecounts-from-any-timepoint-minus-nsteps: type declaration for unknown variable all-events
;   In get-timepoints-from-any-timepoint-minus-nsteps-ignor-gracenotes: type declaration for unknown variable vsolution
;   In get-events-from-any-timepoint-minus-nsteps-ignor-gracenotes: type declaration for unknown variable vsolution
;   In get-timepoints-from-before-any-timepoint-minus-nsteps-ignor-gracenotes: type declaration for unknown variable all-onsets-no-gracenote-no-rest
;   In an anonymous lambda form at position 106661: Duplicate definitions of remove-rests-and-return-notecountlist, in this file
;   In matrix-trans: type declaration for unknown variable row
; Warning: Lisp compilation failed while compiling #<cl-source-file "cluster-engine" "sources/09.utilities">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "cluster-engine" "sources/09.utilities">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/09b.markov-tools.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/09c.cluster-energy-profile.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/12.debug-tools.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/13.convert-pmc-rules.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/_000.main-interface.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-engine/sources/export.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-rules/sources/package.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-rules/sources/utils.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-rules/sources/rhythm-rules.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-rules/sources/melody-rules.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-rules/sources/harmony-rules.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-rules/sources/counterpoint-rules.lisp"...
;Compiling "/Users/acme/common-lisp/cluster-rules/sources/export.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/slippery-chicken.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/utils.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/OMN-utils.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/score.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/rhythm.lisp"...
;Compiler warnings for "home:common-lisp;tot;sources;rhythm.lisp.newest" :
;   In an anonymous lambda form at position 65555: Duplicate definitions of total-duration, in this file and in "home:common-lisp;tot;sources;OMN-utils.lisp.newest"
; Warning: Lisp compilation failed while compiling #<cl-source-file "tot" "sources/rhythm">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "tot" "sources/rhythm">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/tot/sources/pitch.lisp"...
;Compiler warnings for "home:common-lisp;tot;sources;pitch.lisp.newest" :
;   In stretch-pitches: Undeclared free variable chord
; Warning: Lisp compilation failed while compiling #<cl-source-file "tot" "sources/pitch">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
; Warning: Lisp compilation had style-warnings while compiling #<cl-source-file "tot" "sources/pitch">
; While executing: uiop/lisp-build:check-lisp-compile-warnings, in process Listener-1(7).
;Compiling "/Users/acme/common-lisp/tot/sources/velocity.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/articulations.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/PWGL.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/constraints.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/form.lisp"...
;Compiling "/Users/acme/common-lisp/tot/sources/orchestration.lisp"...
:tot
nil
89 > 
:tot
nil
89 > 

 

Share this post


Link to post
Share on other sites

Actually, these are warnings and not errors. They point out various issues in the code that could be improved (in particular in the library cluster engine, as you may have noticed), but the code should still work overall. Also, you should see these compilation and warning messages only once (if you never change the code of the relevant library).

 

Just try to go on and actually run some examples of these libraries. 

 

Please let me know if you run into an actual error message. (There are some minor bugs of course, but at least for my tot library I try to make them explicit in the documentation as well.)

 

Best,

Torsten

Share this post


Link to post
Share on other sites

Good to hear things are working for you 😉

 

Note that dependencies of this library are of interest on their own for computer-aided composition. This is particularly true for the constraint library cluster engine and its extension cluster rules, as well as the library fenv

 

Torsten

Share this post


Link to post
Share on other sites

> I like the Carnatic Stuff.

 

To best understand what those functions offer I recommend the book referenced in the doc (Reina, 2016), which was quite an eye-opener for me. There is a partial preview at Google Books (https://books.google.co.uk/books?id=4RSrCwAAQBAJ&amp;printsec=frontcover#v=onepage&amp;q&amp;f=false), and further information is available at the associated websites (http://www.contemporary-music-through-non-western-techniques.com and http://www.rafaelreina.org/book-online-material.html).

 

Best,

Torsten

 

References

Reina, R. (2016) Applying Karnatic Rhythmical Techniques to Western Music. Routledge.
added 8 minutes later

I just noticed the perhaps most important Karnatic function so far, gen-karnatic-cell, is not contained in the automatically generated documentation, because the documentation tool seemingly skips functions that are not defined at the top level. I may fix that at some stage, but for now you can read the documentation in the doc string of the function in its source file (rhythm.lisp), or you evaluate the following line and read it in the listener.

 

(documentation 'gen-karnatic-cell 'function)

 

Best,

Torsten

 

Share this post


Link to post
Share on other sites

I got just four 16th notes.

 

This function allows for various controls that you did not use – Of course this function also allows for plain sequences of 1/16-notes, when you select its arguments accordingly 🙂   I am interested in CAC as a means of control on a higher level instead of some automatic "magic".

 

You might want to run the examples below in Opusmodus to get music notation outputs, which are likely more easy to read than the OMN expressions, but nevertheless I provide the OMN expressions for completeness as well.

 

The argument position (3rd argument) controls the kind of rhythmic cell that you get, see the doc for details. Here is an example where different rhythmic cells are returned. Note that by default all returned cells carry potentially a durational accent. 

 

(gen-karnatic-cell 4 4 '(0 4 3 2 1 0))
=> ((1/4) (1/16 1/16 1/16 1/16) (1/8 1/16 1/16) (1/8 1/8) (3/16 1/16) (1/4))

 

Now, if you use the same positions but different gati or jathi settings then you get a somewhat similar rhythm in that different gati/jathi. Below I slightly adapted the positions to make the results even more similar. 

 

(gen-karnatic-cell 4 5 '(0 6 3 2 1 0))
=> ((5/16) (1/16 1/16 1/16 1/16 1/16) (3/16 1/16 1/16) (3/16 1/8) (1/4 1/16) (5/16))

 

You can, of course, use these in a different time signature (e.g., the tala of your choice).

 

(omn-to-time-signature (gen-karnatic-cell 4 5 '(0 6 3 2 1 0)) '(4 4))
=> ((q c4 tie s s s s s s e. s s s tie) (e c4 c4 q s q tie s))

 

If you want to keep track of where the accents are located, you could mark them before this transformation (the function articulate-phrase is defined below this post). You could then manually later revise the notation to instead you the beam-breaking that Reina recommends. 

 

(omn-to-time-signature 
 (articulate-phrase (gen-karnatic-cell 4 5 '(0 6 3 2 1 0))
                    :accent 'marc)
'(4 4))

=> ((q c4 tie+marc s marc s marc s s s s e. marc s s s tie+marc) (e c4 c4 q marc s q tie+marc s marc))
 

After the changed jathi above, here is a different gati. Results are more similar, because the underlying data is the same with different gati but the same jathi. Here is an example with quintuplets. 

 

(gen-karnatic-cell 5 4 '(0 4 3 2 1 0))
=> ((1/5) (1/20 1/20 1/20 1/20) (1/10 1/20 1/20) (1/10 1/10) (3/20 1/20) (1/5))

 

Here is the same with triplets -- exactly the same, only slower. 

 

(gen-karnatic-cell 3 4 '(0 4 3 2 1 0))
=> ((1/3) (1/12 1/12 1/12 1/12) (1/6 1/12 1/12) (1/6 1/6) (1/4 1/12) (1/3))


You can also change the jathi (or even gati) on the fly in a phrase. The given pattern is then 'cycled through'.

 

(gen-karnatic-cell 4 '(5 5 3 3) '(0 6 3 2 1 0))

=> ((5/16) (1/16 1/16 1/16 1/16 1/16) (1/16 1/16 1/16) (1/16 1/16 1/16) (1/4 1/16) (5/16))


You can also randomise your position

 

(gen-karnatic-cell 4 5 '(0 1 ? ? 0))

=> ((5/16) (1/4 1/16) (3/16 1/8) (3/16 1/16 1/16) (5/16))


And I did not even start discussing the other parameters yet. For example, you can set whether or not cells start with a durational accent (not a karnatic concept, but very useful notion for Western ears when dealing with rhythm). 

 

(gen-karnatic-cell 4 5 '(3 3 3 3) :accented? '(T nil nil T))

=> ((3/16 1/16 1/16) (1/16 3/16 1/16) (1/16 3/16 1/16) (3/16 1/16 1/16))

 

You can filter in various ways want kind of rhythm you want, e.g., set the minimum or maximum number of notes per cell. 

 

(gen-karnatic-cell 4 5 '(? ? ? ?) :min-number '(3 3 3 3) :seed 1)

=>((1/8 1/8 1/16) (3/16 1/16 1/16) (1/16 1/16 1/16 1/16 1/16) (1/8 1/8 1/16)) 

 

EDIT: Oops, that are is seemingly not fully working as expected.

 

I stop here and refer you do the documentation for more details: there are several more arguments that I did not even mention yet. I know that my documentation is rather concise and not a textbook, but it briefly discusses every detail. I said from the beginning, I wrote this for myself, so you might want to first spend a bit of time with the doc 🙂

 

As you hopefully can see, this function alone allows for a rather rich world of rhythm with rather precise control that allows varying some rhythmic idea in various ways. I am interested here in clearly perceivable rhythmic similarities, e.g., between rhythms in different gati and jathi combinations in the Western tradition of thematic variation.  

 

Of course, you can also process the result further in various ways. E.g., I am adding ties after long notes and/or subdivide short notes to increase the rhythmic contrast, and I am turning certain notes into rests...
 

Best,

Torsten

 

(defun articulate-phrase (sequence &key (accent 'marc))
  "Add articulations to phrase for more clear rhythmic accents, e.g, an accent on every first beat
.
  
  NOTE: This function assumes that `sequence' is a purely rhythmic OMN expression with only length values and perhaps ties. However, a sequence with (constant) pitches is returned, because certain Opusmodus functions do not support an OMN sequence without pitches.

  Examples:
  (articulate-phrase '((h h) (q q q q tie) (q q q q) (-q q) (q q q q)) :accent 'marc)
  => ((h marc h) (q marc q q q tie) (q q q q) (-q q) (q marc q q q))
  "
  (cons 
   ;; First bar
   ;;; NOTE: code repetition...
   (let ((bar1 (first sequence)))
     (if (length-notep (first bar1))
       (if (and (= (count-notes bar1) 1)
                (eql (first (last bar1)) 'tie))
         (tu:replace-element (merge-articulations (list accent 'tie)) 1 bar1)
         (tu:insert-after bar1 0 accent))
       bar1))
   ;; other bars
   (loop :for (bar1 bar2) :on sequence :while bar2
     ;;; NOTE: code repetition
     :collect (if (and (length-notep (first bar2))
                       (not (eql (first (last bar1)) 'tie)))
                (if (and (= (count-notes bar2) 1)
                         (eql (first (last bar2)) 'tie))
                  (tu:replace-element (merge-articulations (list accent 'tie)) 1 bar2)
                  (tu:insert-after bar2 0 accent))
                bar2))))

 

Share this post


Link to post
Share on other sites
5 hours ago, lviklund said:

This is brilliant!

Thx Torsten and Julio.

 

Thanks to Torsten! 

I'm the apprentice 😀

😎

Share this post


Link to post
Share on other sites

Wow! Just installed and ran some examples from your docs...thank you Torsten for sharing this, and all your hard work to document and help install.

 

I will simply echo "lviklund" and say, "This is brilliant!" ;-)

Share this post


Link to post
Share on other sites

all your hard work to document

 

I actually do such detailed documentation for my own benefit as well. I once spent several months on developing some personal library that I used for composing some piece, but when I wanted to re-use it some two years later, I could not really understand my own functions anymore. Since then I better err on the side of overdoing the docs 🙂

 

Best,

Torsten

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

  • Similar Topics

    • By opmo
      – Function name changes:
      LENGTH-DIVIDE2 to LENGTH-SUBDIVISION
      LENGTH-DIVIDE3 to LENGTH-SYNCOPATE

      – Function update:
      LENGTH-DIVIDE – changes to arguments.
      LENGTH-SUBDIVISION – changes to arguments.
      LENGTH-SYNCOPATE – changes to arguments.
      POLYGON-RHYTHM – enable fixed sides polygon. 

      – Note:
      If you used any of the functions:
      LENGTH-DIVIDE, LENGTH-DIVIDE2 or LENGTH-DIVIDE3 in your scores,
      please check new documents in order to make the necessary correction.

      – New:
      Enable or disable the DO-VERBOSE macro. 
      (defparameter *do-verbose* nil "Enable or disable traces printed by do-verbose.")  
       
      length-divide
      This function is able to divide number of lengths to a given division value. The :set and :ignore option increases the control for the desired result. When processing the omn-form sequence an optional third value allows you to fill intervalic steps (a root transposition) to new length values derived from the divisions.
       
      (setf rhy '(1/4 1/4 1/4 1/4)) (length-divide '(2 2) rhy) => (1/8 1/8 1/4 1/4 1/8 1/8) (length-divide '(2 4) rhy) => (1/4 1/16 1/16 1/16 1/16 1/16 1/16 1/16 1/16 1/4)  
      Example:
      (length-divide '(1 2) '(1/4 -1/8 1/16 1/16 -1/32 -3/32 1/8 1/1) :seed 34) => (1/4 -1/8 1/16 1/32 1/32 -1/32 -3/32 1/8 1)  
      In the example above only 1 length is divided by 2 - that is the 1/16. In the example below 4 lengths are divided by 2.
      (length-divide '(4 2) '(1/4 -1/8 1/16 1/16 -1/32 -3/32 1/8 1/1) :seed 34) => (1/8 1/8 -1/8 1/16 1/32 1/32 -1/32 -3/32 1/16 1/16 1/2 1/2) (length-divide '(1 2) '(1/4 -1/8 1/16 1/16 -1/32 -3/32 1/8 1/1) :set 'min :seed 34) => (1/4 -1/8 1/32 1/32 1/16 -1/32 -3/32 1/8 1) (length-divide '(1 4) '(1/4 -1/8 1/16 1/16 -1/32 -3/32 1/8 1/1) :set 1/8 :seed 34) => (1/4 -1/8 1/16 1/16 -1/32 -3/32 1/32 1/32 1/32 1/32 1) (length-divide '((2 3) (1 2)) '((1/4 -1/8 1/16 1/16) (1/32 -3/32 1/8 1/1)) :ignore 'max :seed 45) => ((1/4 -1/8 1/48 1/48 1/48 1/48 1/48 1/48) (1/64 1/64 -3/32 1/8 1)) (length-divide '((2 4) (1 2)) '((q -e s s) (s -e. e w)) :set 'max :ignore 1 :seed 65) => ((1/16 1/16 1/16 1/16 -1/8 1/16 1/64 1/64 1/64 1/64) (1/16 -3/16 1/16 1/16 1))
      OMN:
      (setf mat1 '(q c4 d4 e4 f4 g4 a4 b4)) (length-divide '(3 4) mat1 :seed 45) => (s d4 bb3 cs4 b3 cs4 eb4 c4 e4 q s g4 e4 eb4 fs4 q g4 a4 b4)
      Symbol 'r will apply repeat function:
      (length-divide '(3 4 r) mat1 :seed 45) => (s c4 c4 c4 c4 d4 d4 d4 d4 q e4 s f4 f4 f4 f4 q g4 a4 b4)
      Here we use a set of interval values at the end of the division list:
      (length-divide '(3 4 (13 0 1 13)) mat1 :seed 45) => (s cs5 c4 cs4 cs5 eb5 d4 eb4 eb5 q e4 s fs5 f4 fs4 fs5 q g4 a4 b4) (setf mat2 '((e c4 p e4 mp g4 he c5 p) (q c4 f c4 cs4 mp - d5 p d5) (q cs5 mf = - - cs5 p =))) (length-divide '((1 4) (2 4) (2 5)) mat2 :seed 34) => ((e c4 p e4 mp t a4 f4 gs4 fs4 he c5 p) (q c4 f s b3 cs4 bb3 d4 q cs4 mp - d5 p s c5 e5 cs5 eb5) (q cs5 mf cs5 - - 5q eb5 p b4 c5 d5 eb5 c5 eb5 b4 d5 c5))
      In the example below we assign three series of division values to variables s1, s2 and s3:
      (setf s1 '(3 4 (6 12 18 24)) s2 '(3 4 ((13 1 13 0) (13 0 7 1) r)) s3 '(2 5 ((13 0 13 0 13) ?)) ) (length-divide (list s1 s2 s3) mat2 :seed 34) => ((e c4 p t bb4 mp e5 bb5 e6 cs5 g5 cs6 g6 et fs5 p c6 fs6 c7) (q c4 f s cs5 cs4 cs5 c4 q cs4 mp - s eb6 p d5 a5 eb5 d5 d5 d5 d5) (5q d6 mf cs5 d6 cs5 d6 q cs5 - - cs5 p 5q d5 eb5 c5 b4 d5))  
       
      length-subdivision
      This function is able to divide a list of lengths into a number of subdivisions derived from a given length segment value. The :type and :position option increases the control for the desired result. When processing the omn-form sequence an optional third value allows you to fill intervalic steps (a root transposition) to new length values derived from the divisions. This function is a more sophisticated version of LENGTH-DIVIDE. It produces fascinating variants on the simplest of note-lengths, as can be seen below.
       
      (setf rhy '(1/4 1/4 1/4 1/4)) (length-subdivision '(2 1/8) rhy) => (1/8 1/8 1/8 1/8 1/8 1/8 1/8 1/8)
      position 's (start):
      (length-subdivision '(2 1/16) rhy :position 's) => (1/16 1/16 1/8 1/16 1/16 1/8 1/16 1/16 1/8 1/16 1/16 1/8)
      position 'e (end):
      (length-subdivision '(2 1/16) rhy :position 'e) => (1/8 1/16 1/16 1/8 1/16 1/16 1/8 1/16 1/16 1/8 1/16 1/16)
      type 'r (rest), position 'e (end):
      (length-subdivision '(2 1/16) rhy :type 'r :position 'e) => (-1/8 1/16 1/16 -1/8 1/16 1/16 -1/8 1/16 1/16 -1/8 1/16 1/16)
      type 'r (rest), position 's (end):
      (length-subdivision '(2 s) rhy :type 'r :position 's) => (1/16 1/16 -1/8 1/16 1/16 -1/8 1/16 1/16 -1/8 1/16 1/16 -1/8)
      type at random, rest or note :
      (length-subdivision '(2 s) rhy :type '? :position 's) => (1/16 1/16 -1/8 1/16 1/16 -1/8 1/16 1/16 -1/8 1/16 1/16 1/8)
      position and type at random:
      (length-subdivision '(1 e) rhy :type '? :position '? :seed 34) => (1/16 1/8 1/16 1/8 1/8 -1/8 1/8 1/8 1/8) (length-subdivision '((2 5q) (1 3q)) rhy :type '? :position 's :seed 34) => (1/20 1/20 3/20 1/12 -1/6 1/20 1/20 3/20 1/12 -1/6) (length-subdivision '((2 5q) (1 3q) (1 e) (1 s)) rhy :seed 34) => (1/20 1/20 3/20 1/12 1/12 1/12 1/8 1/8 3/16 1/16)  
      Example:
      (setf rhy2 '((1/4 1/4 1/4 1/4) (1/4 1/4 1/4 1/4))) (length-subdivision '(1 e) rhy2 :seed 34) => ((1/8 1/8 1/16 1/8 1/16 1/8 1/8 1/8 1/8) (1/16 1/8 1/16 1/8 1/8 1/8 1/8 1/8 1/8)) (length-subdivision '((1 e) (1 3q)) rhy2 :seed 34) => ((1/8 1/8 1/12 1/12 1/12 1/8 1/8 1/6 1/12) (1/16 1/8 1/16 1/6 1/12 1/8 1/8 1/6 1/12)) (length-subdivision '(((1 e)) ((1 3q))) rhy2 :seed 34) => ((1/8 1/8 1/16 1/8 1/16 1/8 1/8 1/8 1/8) (1/12 1/12 1/12 1/6 1/12 1/12 1/6 1/6 1/12)) (length-subdivision '((3 3q) (1 e)) '((q -e e h) (s e. q h)) :seed 65) => ((1/12 1/12 1/12 -1/8 1/8 1/12 1/12 1/12 1/4) (1/16 3/16 1/8 1/8 1/8 1/12 1/12 1/12 1/8)) (length-subdivision '(((3 3q)) ((1 e))) '((q -e e h) (s e. q h)) :seed 65) => ((1/12 1/12 1/12 -1/8 1/8 1/12 1/12 1/12 1/4) (1/16 1/16 1/8 1/16 1/8 1/16 1/8 3/8)) (length-subdivision '(((2 3q)) ((1 e))) '((q -e e h) (s e. q h)) :type '? :seed 65) => ((1/12 1/12 1/12 -1/8 1/8 1/12 1/12 -1/3) (1/16 -1/16 1/8 1/8 1/8 1/8 3/8))
      OMN:
      (setf mat1 '(q c4 d4 e4 f4 g4 a4 b4)) (length-subdivision '(1 e) mat1 :seed 45) => (s cs4 e b3 s d4 e cs4 e4 s f4 e fs4 s d4 e fs4 eb4 f4 a4 bb4 gs4 bb4 a4)
      The symbol 'r (third value) will apply repeat function:
      (length-subdivision '(1 e r) mat1 :seed 45) => (s c4 e s e d4 d4 s e4 e s e f4 f4 g4 g4 a4 a4 b4 b4)
      Here we define the intervals (third value):
      (length-subdivision '(1 e (13 0 13 0)) mat1 :seed 45) => (s cs5 e c4 s cs5 e eb5 d4 s f5 e e4 s f5 e fs5 f4 gs5 g4 bb5 a4 c6 b4) (length-subdivision '(4 s (13 0 13 0)) mat1 :seed 45) => (s cs5 c4 cs5 c4 eb5 d4 eb5 d4 f5 e4 f5 e4 fs5 f4 fs5 f4 gs5 g4 gs5 g4 bb5 a4 bb5 a4 c6 b4 c6 b4) (length-subdivision '(2 3q (13 0 13 0)) mat1 :type '(r n) :seed 45 :position '(e s s s e s s)) => (-3q cs5 c4 eb5 d4 eb5 f5 e4 - fs5 f4 fs5 - gs5 g4 bb5 a4 bb5 c6 b4 -) (setf mat2 '((e c4 p e4 mp g4 he c5 p) (q c4 f c4 cs4 mp - d5 p d5) (q cs5 mf = - - cs5 p =))) (length-subdivision '((1 e (13 0 13 0)) (2 e (13 0 13 0)) (2 3q (13 0 13 0))) mat2 :type '? :seed 34) => ((e c4 p e4 mp g4 q cs6 p e c5 q cs6) (e cs5 f c4 cs5 c4 -3q d5 mp cs4 -q e eb6 p d5 eb6 d5) (-s e cs5 mf -s e d6 cs5 -q - 3e d6 p 3q cs5 d6 3e cs5 e d6 cs5)) (length-subdivision '(((1 e (13 0 13 0))) ((2 s (13 0 13 0))) ((2 3q r))) mat2 :type '? :seed 34) => ((e c4 p e4 mp g4 q cs6 p e c5 q cs6) (e cs5 f s c4 cs5 -e s cs5 c4 e d5 mp s cs4 d5 -q -s eb6 p d5 - eb6 d5 eb6 d5) (3q cs5 mf cs5 cs5 cs5 cs5 cs5 -q - 3q cs5 p cs5 - - cs5 cs5))  
      In the example below we assign three series of values to variables s1, s2 and s3:
      (setf s1 '(2 e (6 12 18 24)) s2 '(1 3q ((13 1 13 0) (13 0 7 1) r)) s3 '(3 5q ((13 0 13 0 13) ?)) ) (length-subdivision (list s1 s2 s3) mat2 :seed 23) => ((e c4 p 3e bb4 mp 3q e5 e cs6 cs5 p cs6 q.) (e fs4 f c5 3q cs5 cs4 cs5 5h mp 5q c4 g4 c4 -q e c4 p c4 3q d5 3h cs4) (e g5 mf cs6 3q d6 3h d5 -q - 5q d6 p cs5 cs5 5h e d6 cs5))  
       
       
      length-syncopate
      The function LENGTH-SYNCOPATE is a valuable way of bringing more rhythmic interest into a length list. The usual idea of syncopating rhythm is to 'choke' certain attacks so that the attack is delayed or pre-empted.
      (setf rhy '(1/4 1/4 1/4 1/4)) (length-syncopate '(1 4) rhy) => (1/4 3/16 1/16 1/4 1/4) (length-syncopate '(2 4) rhy) => (1/16 3/16 1/4 3/16 1/16 1/4)  
      Example:
      (length-syncopate '(1 4) '(1/4 -1/8 1/16 1/16 -1/8 1/8 1/1) :seed 34) => (1/4 -1/8 1/16 1/64 3/64 -1/8 1/8 1)
      In the example above only 1 length is divided by 4 (1, 3) - that is the 1/16. In the example below 2 values are divided by 3: (1, 2) and (2, 1).
      (length-syncopate '(2 3) '(1/4 -1/8 1/16 1/16 -1/8 1/8 1/1) :seed 34) => (1/4 -1/8 1/48 1/24 1/16 -1/8 1/8 2/3 1/3) (length-syncopate '(1 4) '(1/4 -1/8 1/16 1/16 -1/8 1/8 1/1) :set 1/8 :seed 34) => (1/4 -1/8 1/16 1/16 -1/8 1/32 3/32 1)
      Example with :set for each list:
      (length-syncopate '((2 3) (1 4)) '((1/4 -1/8 1/16 1/16) (1/32 -3/32 1/8 1/1)) :set '(min 1/8) :seed 45) => ((1/4 -1/8 1/24 1/48 1/24 1/48) (1/32 -3/32 3/32 1/32 1)) (length-syncopate '((2 3) (1 5)) '((q -e s s) (s -e. q h)) :set 'max :ignore 'h :seed 65 :omn t) => ((3h 3q -e s 3s 3e) (s -e. 5q 5w h))
      OMN:
      (setf mat '(q c4 d4 e4 f4 g4 a4 b4)) (length-syncopate '(3 4) mat :seed 12) => (s b3 e. cs4 q d4 e. fs4 s d4 q f4 g4 a4 e. bb4 s c5)  
      Here we use a set of interval values:
      (length-syncopate '(3 4 ((13 0) (0 14) (1 13))) mat :seed 23) => (s cs5 e. c4 d4 s e5 q e4 f4 s gs4 e. gs5 q a4 b4) (setf mat2 '((e c4 p e4 mp g4 he c5 p) (q c4 f c4 cs4 mp - d5 p d5) (q cs5 mf = - q cs5 stacc p = =)) (length-syncopate '((1 3 (-3 6)) (2 4 (6 0)) (2 5 (11 13))) mat2 :seed 34) => ((e c4 p e4 mp 3e 3q cs5 he c5 p) (q c4 f s fs4 e. c4 q cs4 mp - e. gs5 p s d5 q) (q cs5 mf cs5 - 5w c6 stacc 5q d6 stacc q cs5 p 5q c6 5w d6 q cs5))
       
       
      polygon-rhythm
      In the next three examples below we use a list of fixed polygon sides (non-symmetrical):
      (circle-rhythm-plot (polygon-rhythm '(1 6 10) 16 1) :points 16) To rotate the polygon we change the starting point value:
      (circle-rhythm-plot (polygon-rhythm '(1 6 10) 16 2) :points 16) (circle-rhythm-plot (polygon-rhythm '(0 2 5 7 10 12 13 15 16 18 19 21 23) 24 0) :points 24 :value 1/24)  
      Best wishes,
      JP
    • By torstenanders
      The function LENGTH-DIVIDE and friends are a useful device for introducing some rhythmic variation to some material. However, when the processed sequence is a full OMN expression (see example below), then the added notes cause all other parameters to shift forward (additional parameters are added at the end by looping). For introducing only a slight variation to some existing material (e.g., motif) these functions are therefore not so useful, because this shift of all parameters to basically "fill" the new added notes with pitches etc. greatly change the varied material for a listener.
      (length-divide 1 2 '(q f3 leg+m1 q ab3 leg e c4 leg bb3 leg ab3 leg g3))  
      Basically, this is the opposite situation of the situation addressed by swallowing. Swallowing (e.g., with GEN-SWALLOW and the swallow argument of many other functions) avoids the shift of parameters like pitch etc. that would result from removing notes and turning them into rests. For addressing the situation above properly we would need something like the opposite of swallowing, some way to fill in parameters like pitch etc. for new notes to avoid shifting existing notes. I hope my explanation makes sense and you can see why something like the opposite of swallowing would be useful for functions that add notes to some input material.
       
      Now, the tricky question is of course, what parameters (pitch etc.) should be added in such cases. Musically useful and common in the classical literature would be, e.g., repeating the parameters (pitch etc.) of the predecessor note (though that might be a bit too simplistic and annoying at times) or somehow interpolating some pitch gap between the previous and next note with a little scale or arpeggio and only repeating the other parameters like articulations (resulting in some variation that is likely most similar to the input material, so it would be good to have that as an option).  If the pitch interval between the two original notes is too small to add some pitch in between for the new now (or as an option in general for variety), it would also be good  to add some ornamentation (e.g., using PITCH-ORNAMENT), but for a close similarity between the original material and the variation it would be best as an option to apply such ornamentation only to the newly added notes, not all notes. Of course, other options could also be useful for variations that vary the input material a bit more strongly, e.g., some random pitch for the new notes within certain interval bounds.
       
      Does some function that kind of is the opposite of GEN-SWALLOW in that it adds parameters like pitches for subdivided rhythmic values (to avoid the shifting) sound like a good idea?
       
      The idea with the interpolation of gaps could be implemented by turning the original duration values and pitches into some envelope and then reading that envelope with the new rhythmic values. So, that is rather strait forward. However, that would not allow for some ornamentation, because such algorithm would not recognise which notes are new that should be ornamented.
       
      Any other idea perhaps? Thanks! 
    • By opmo
      – Additions:
      ADD-TEXT-ATTRIBUTS – Optional attribute type: :sticky and :non-sticky

      – Fixed:
      Typo error in 'GM Instrument Set.lisp' file.
       
      Best wishes,
      Janusz
×
×
  • Create New...