nanc-in-a-can / canon-generator Goto Github PK
View Code? Open in Web Editor NEWCreate and visualize temporal canons a'la Conlon Nancarrow
Create and visualize temporal canons a'la Conlon Nancarrow
The visualize code is very sexy and all that but it doesn´t show nearly enough information that it could be showing that might provide a more complete experience of the possibilites of temporal canons. We can think of two fundamental sets of data, the first is the ecoic distance between voices. This can be easily implemented however a cool visual representation is still pending as it would mean additional parameters to the visualize function.
If doing something like this:
(
var melody = ~presets.pyramidalMelody;
var canon = ~convCanon.(melody).canon
.collect(~instrument.([\pianola]))
Pdef(\canon, Ppar(canon, inf))
)
Pdef(\a).play
It could be nice to have a standard recommendation for what to do when you recompile the code with some change but do not want the new canon to start playing at the beginning.
How should this look like?
This issue is related to issue #9 however it is much more complicated given the ambiguous and complex nature of such concept. The question is, how to visualise the concept of temporal disonance without diregarding the perceptive, acoustic, musical and formal aspects of its definition? For further information about this concept this might be a great reading.
Add a description
How to run the program
How to choose a preset
How to add your own melody, with tempos, transpositions and convergence point
When compiling ThePresetCan.nancarrowStudy14.visualize(s)
it does not visualise or reproduce the canon. A problem with the method mergeCanon might be what is causing the error.
It would be cool to have a way to override the global cp
provided to the ~convCanon
function and have custom cp
s for each voice.
How would the API look like?
Add to a separate repo
We can think of the transposition of a melodic sequence as the application of a sum function that adds a positive or negative interval to each note of a melody:
//more strictly we could write it like this:
[60, 62, 63].collect({|note| note+7}); // [67, 69, 70]
// although the usual shorthand is this:
[60, 62, 63]+7
We could then think that transposition is actually the mapping of melody through a function into another melody.
Given that, we could think of more complex and possibly interesting forms of transposition:
//adding a harmonic major third to every note in the the melody
[60, 62, 63]+[[0, 4]]; // [ [ 60, 64 ], [ 62, 66 ], [ 63, 67 ] ]
// or to every second note
[60, 62, 63]+[0, [0, 4]]; // [ 60, [ 62, 66 ], 63 ]
//or transposing with a modulo rule, for example, alternating between a seventh and a minor third
[60, 62, 63]+[7, 3]; // [67, 65, 70]
We could have an API the would look something like this:
~convCanon.((
voices: [
(tempo: 80, transp: 7), // default sum
(tempo: 100, transp: {|note| note+7}), // explicit sum, would render the same result as above
(tempo: 100, transp: {|note| note+[14, 19]}),// an explicit function
(tempo: 100, transp: _+[14, 19]),// a function using the partial application syntax, which is rather nice
]
));
Perhaps the function could take not only the current note
, but also its index
, the whole melody
and the whole durations
sequence, so that even more complex and context aware functions could be implemented.
So then:
~convCanon.((
voices: [
(tempo: 80, transp: {|note, index, melody, durations| /*some crazy implementation*/}),
]
));
Then the types that the transp
key would take would be:
Transp ::
Interval -- for the the default sum
| (Note, Index, Melody, Durations) -> Note -- Index, Melody, Durations are all optional
One edgecase that I can think of is this:
In an example like this:
[60, 62, [63, 65]]+[[0, 4]];
Should [63, 65]
become [ 63, 69 ]
, as SuperCollider would output by default, or should it mean something like [63, 66, 65, 68]
.
Perhaps a helper for cases such as this should be provided, but it seems like a good idea leave SCs defaults as they are.
One main feature of the work of Conlon Nancarrow is the use of proportional tempo based on the idea of harmonic rhythm described by Henry Cowell in the book New Musical Resources. It might be great to have a helper function that allows users to algorithmically design a tempo array that simultaneously produces transposition based in the proportional relationships formed by such tempos. We already have a function that might convert tempo into transposition values however it doesn´t perform the opposite operation and the transpositions are expressed in absolute values that will conflict with the values of the melody as it is presently designed. It also requires an octave
/ rhythm figure
parameter to freely choose the register of its transposition/tempo.
(
~makeHarmRhythm= { | tempos, transp, proportions|
var
propTempos= tempos*(proportions ? [1]),
defaultProportions = if(proportions == nil,
{tempos.cpsmidi},
{propTempos.cpsmidi}
),
tempoToTransp= if(transp != nil,
{transp},
{defaultProportions},
),
resultingTempos = if(proportions == nil, {tempos}, {propTempos}),
result = resultingTempos.size.collect({|i|
(tempo: resultingTempos[i], transp: tempoToTransp[i])
});
//Post warnings on inconsistent data
if(proportions != nil && tempos.isArray, {"inconsistent tempos declared, tempos may be weird".warn});
if( tempos.isArray && tempos.size == 1, {"inconsistent tempos declared, tempos may be weird".warn});
result
}
)
// try me
~makeHarmRhythm.(tempos: [ 180, 240, 300, 360 ]);
// just arbitrary tempo, automatically generates transp with harmonic rhythm principle
~makeHarmRhythm.(tempos: 60, proportions: [3,4,5,6]);
// tempos generated proportionally, automatically generates transp with harmonic rhythm principle
~makeHarmRhythm.(tempos: 20, proportions: [3,4,5,6], transp: [12, 24, -12, -15]);
// tempos generated proportionally, transposition values generated arbitrarily
~makeHarmRhythm.(tempos: [ 180, 240, 300, 360 ], transp: [12, 24, -12, -15]);
// tempo and transposition generated arbitrarily
This function needs to work with the idea of transposition as function value proposed in issue #2 . It also requires an API that doesn´t 'convert' tempo to transposition but it is a new parameter that can be named and invoked in a new amazing and fantastic way.
The canonic structure that might be most famous and attractive for Nancarrow´s fans is the acceleration canon, as the "X" canon also known as Study 21. To design an algorithm that express the principles of the acceleration canons is challenging, it also requires to think of an API that is simple and intuitive enough so acceleration canons might be a new tool for live coding. We would welcome any ideas for cool APIs!
In Can.converge
A SuperCollider Class could be made out of this library of functions.
It does not serve any purpose and is boilerplate code.
The current API looks like this because of the implementation of ~visualize
, but it could and should be solved so that this piece of boilerplate is no longer necessary.
Positive offsets at first, them also negative
Design API: static data and also a function
Document
By using Ptpar
class it is possible to create multiple canon structures embedded in Pdef
s. It would be great if it wouldn´t look so horrible when live coding with it:
(
~config1 = (
cp: 10,
melody:
~makeMelody.(
(4..16).reciprocal.scramble,
[60, 67, [68, 65], [63, 55], [62, 59, 53], [58, 70, 82], [36,48,60,72]].mirror.reverse
),
voices:
~makeConvVoices.(
Array.series(7, 50, 15)*0.5, // tempos
Array.series(7, -30, 8.3).scramble // transp
)
);
~config2 = (
baseTempo: 20,
voices:
~makeDivVoices.(
Array.series(13, 0, 0.1)++[-20] // transp
),
melody:
~makeMelody.(
(4..7).stutter(2).reciprocal.scramble,
[60, 67.5, 68, 63, 62.3, 58.5, 60]
),
tempos:
~makeDivTempos.(
Array.series(14, 30, 13), // tempos
[1, 2, 5, 7, 8, 10, 9, 3, 3, 5, 4, 3, 2, 3], // percentages
normalize: true
)
);
~canon1= ~convCanon.(~config1);
~c1= ~canon1
.canon
.collect(~instrument.([\pianola], amp: 1, repeat: 1));
~c2= ~divCanon.(~config2)
.canon
.collect(~instrument.([\pianola], amp: 1, repeat: 1));
Pdef(\myCanonLoop, Ptpar(
[0, Ppar(~c1, 1), ~canon1.canon[6].durs.sum*0.75, Ppar(~c2, 1)]
, inf ));
);
Pdef(\myCanonLoop).play
The key issue here is how to design an API that could allow a better and clearer interface for the onset time of each canon. Even further, how to correlate each canon by creating a convergence between them like: 7thnote-1stvoice-1stcanon converging with 11thnote-2ndvoice-2ndcanon.
Now (as of commit 02dbd94) that canons can have instrument and player keys, there should be a proper way to handle the merger of canons.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.