From d41edd2abcaa97ff6e576193a512b66a4a9d598c Mon Sep 17 00:00:00 2001 From: mwinter Date: Fri, 30 Jun 2023 20:10:40 +0200 Subject: [PATCH] string quartet 1 initial stitch. TODO: make each ledger have its own resource folder so I can implement pruning --- lilypond/includes/part_I.ly | 62 +- lilypond/includes/part_II.ly | 62 +- lilypond/includes/part_III.ly | 62 +- lilypond/includes/part_IV.ly | 62 +- lilypond/score_template.pdf | Bin 203809 -> 806447 bytes .../seeds_and_ledgers_mixer.json | 358 ------ resources/445b7057/445b7057_code.scd | 1058 +++++++++++++++++ resources/445b7057/445b7057_mus_model.json | 97 ++ resources/46985d14/46985d14_code.scd | 1058 +++++++++++++++++ resources/46985d14/46985d14_mus_model.json | 86 ++ resources/46985d14/lilypond/part_I.ly | 40 + resources/46985d14/lilypond/part_II.ly | 40 + resources/46985d14/lilypond/part_III.ly | 40 + resources/46985d14/lilypond/part_IV.ly | 40 + resources/490b1e6e/490b1e6e_code.scd | 1058 +++++++++++++++++ resources/490b1e6e/490b1e6e_mus_model.json | 63 + resources/490b1e6e/lilypond/part_I.ly | 28 + resources/490b1e6e/lilypond/part_II.ly | 28 + resources/490b1e6e/lilypond/part_III.ly | 28 + resources/490b1e6e/lilypond/part_IV.ly | 28 + resources/4a8a6e53/4a8a6e53_code.scd | 1058 +++++++++++++++++ resources/4a8a6e53/4a8a6e53_mus_model.json | 97 ++ resources/4a8a6e53/lilypond/part_I.ly | 56 + resources/4a8a6e53/lilypond/part_II.ly | 56 + resources/4a8a6e53/lilypond/part_III.ly | 56 + resources/4a8a6e53/lilypond/part_IV.ly | 56 + resources/535cc132/535cc132_code.scd | 1058 +++++++++++++++++ resources/535cc132/535cc132_mus_model.json | 91 ++ resources/64b535ef/64b535ef_code.scd | 1058 +++++++++++++++++ resources/64b535ef/64b535ef_mus_model.json | 97 ++ resources/652c77ba/652c77ba_code.scd | 1058 +++++++++++++++++ resources/652c77ba/652c77ba_mus_model.json | 97 ++ resources/66f6a618/66f6a618_code.scd | 1058 +++++++++++++++++ resources/66f6a618/66f6a618_mus_model.json | 91 ++ resources/66f6a618/lilypond/part_I.ly | 110 ++ resources/66f6a618/lilypond/part_II.ly | 110 ++ resources/66f6a618/lilypond/part_III.ly | 110 ++ resources/66f6a618/lilypond/part_IV.ly | 110 ++ resources/6fb60ab6/6fb60ab6_code.scd | 1058 +++++++++++++++++ resources/6fb60ab6/6fb60ab6_mus_model.json | 60 + resources/761e4585/761e4585_code.scd | 1058 +++++++++++++++++ resources/761e4585/761e4585_mus_model.json | 81 ++ resources/761e4585/lilypond/part_I.ly | 36 + resources/761e4585/lilypond/part_II.ly | 36 + resources/761e4585/lilypond/part_III.ly | 36 + resources/761e4585/lilypond/part_IV.ly | 36 + resources/7cc3121d/7cc3121d_code.scd | 1058 +++++++++++++++++ resources/7cc3121d/7cc3121d_mus_model.json | 91 ++ resources/7ebbb471/7ebbb471_code.scd | 1058 +++++++++++++++++ resources/7ebbb471/7ebbb471_mus_model.json | 60 + resources/7ebbb471/lilypond/part_I.ly | 24 + resources/7ebbb471/lilypond/part_II.ly | 24 + resources/7ebbb471/lilypond/part_III.ly | 24 + resources/7ebbb471/lilypond/part_IV.ly | 24 + resources/piece_ledger_sq1_candidates.json | 3 +- .../piece_ledger_sq1_candidates.json_bak | 13 + .../piece_ledger_sq1_candidates_stitch.json | 11 + ...iece_ledger_sq1_candidates_stitch.json_bak | 11 + resources/tmp/tmp_mus_model.json | 82 +- supercollider/material_tweak.scd | 99 ++ 60 files changed, 15065 insertions(+), 643 deletions(-) create mode 100644 resources/445b7057/445b7057_code.scd create mode 100644 resources/445b7057/445b7057_mus_model.json create mode 100644 resources/46985d14/46985d14_code.scd create mode 100644 resources/46985d14/46985d14_mus_model.json create mode 100644 resources/46985d14/lilypond/part_I.ly create mode 100644 resources/46985d14/lilypond/part_II.ly create mode 100644 resources/46985d14/lilypond/part_III.ly create mode 100644 resources/46985d14/lilypond/part_IV.ly create mode 100644 resources/490b1e6e/490b1e6e_code.scd create mode 100644 resources/490b1e6e/490b1e6e_mus_model.json create mode 100644 resources/490b1e6e/lilypond/part_I.ly create mode 100644 resources/490b1e6e/lilypond/part_II.ly create mode 100644 resources/490b1e6e/lilypond/part_III.ly create mode 100644 resources/490b1e6e/lilypond/part_IV.ly create mode 100644 resources/4a8a6e53/4a8a6e53_code.scd create mode 100644 resources/4a8a6e53/4a8a6e53_mus_model.json create mode 100644 resources/4a8a6e53/lilypond/part_I.ly create mode 100644 resources/4a8a6e53/lilypond/part_II.ly create mode 100644 resources/4a8a6e53/lilypond/part_III.ly create mode 100644 resources/4a8a6e53/lilypond/part_IV.ly create mode 100644 resources/535cc132/535cc132_code.scd create mode 100644 resources/535cc132/535cc132_mus_model.json create mode 100644 resources/64b535ef/64b535ef_code.scd create mode 100644 resources/64b535ef/64b535ef_mus_model.json create mode 100644 resources/652c77ba/652c77ba_code.scd create mode 100644 resources/652c77ba/652c77ba_mus_model.json create mode 100644 resources/66f6a618/66f6a618_code.scd create mode 100644 resources/66f6a618/66f6a618_mus_model.json create mode 100644 resources/66f6a618/lilypond/part_I.ly create mode 100644 resources/66f6a618/lilypond/part_II.ly create mode 100644 resources/66f6a618/lilypond/part_III.ly create mode 100644 resources/66f6a618/lilypond/part_IV.ly create mode 100644 resources/6fb60ab6/6fb60ab6_code.scd create mode 100644 resources/6fb60ab6/6fb60ab6_mus_model.json create mode 100644 resources/761e4585/761e4585_code.scd create mode 100644 resources/761e4585/761e4585_mus_model.json create mode 100644 resources/761e4585/lilypond/part_I.ly create mode 100644 resources/761e4585/lilypond/part_II.ly create mode 100644 resources/761e4585/lilypond/part_III.ly create mode 100644 resources/761e4585/lilypond/part_IV.ly create mode 100644 resources/7cc3121d/7cc3121d_code.scd create mode 100644 resources/7cc3121d/7cc3121d_mus_model.json create mode 100644 resources/7ebbb471/7ebbb471_code.scd create mode 100644 resources/7ebbb471/7ebbb471_mus_model.json create mode 100644 resources/7ebbb471/lilypond/part_I.ly create mode 100644 resources/7ebbb471/lilypond/part_II.ly create mode 100644 resources/7ebbb471/lilypond/part_III.ly create mode 100644 resources/7ebbb471/lilypond/part_IV.ly create mode 100644 resources/piece_ledger_sq1_candidates.json_bak create mode 100644 resources/piece_ledger_sq1_candidates_stitch.json create mode 100644 resources/piece_ledger_sq1_candidates_stitch.json_bak create mode 100644 supercollider/material_tweak.scd diff --git a/lilypond/includes/part_I.ly b/lilypond/includes/part_I.ly index 7fed1fb..3f9d578 100644 --- a/lilypond/includes/part_I.ly +++ b/lilypond/includes/part_I.ly @@ -1,56 +1,6 @@ -{ - { r2. e'4^\markup { \pad-markup #0.2 "-41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↓" }} ~ } - \bar "|" - { e'2 e'4^\markup { \pad-markup #0.2 "-14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }} ~ e'8.[ f'16^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ } - \bar "|" - { f'4 ~ f'8[ g'8^\markup { \pad-markup #0.2 "+2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ g'4 ~ g'8[ ais'8^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↑" }}] ~ } - \bar "|" - { ais'4 ~ ais'8[ c''8^\markup { \pad-markup #0.2 "+0"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }}] ~ c''2 } - \bar "|" \break - { gis'4^\markup { \pad-markup #0.2 "+41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↑" }} ~ gis'8.[ gis'16^\markup { \pad-markup #0.2 "+14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↓" }}] ~ gis'4 ~ gis'8[ f'8^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ } - \bar "|" - { f'2 e'4^\markup { \pad-markup #0.2 "-14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }} ~ e'8[ r8] } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" \break - { r1 } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" - { r2 r16[ dis'8.^\markup { \pad-markup #0.2 "-38"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↓" }}] ~ dis'4 ~ } - \bar "|" \break - { dis'1 ~ } - \bar "|" - { dis'8[ dis'8^\markup { \pad-markup #0.2 "-11"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↑" }}] ~ dis'2 d'4^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↑" }} ~ } - \bar "|" - { d'1 ~ } - \bar "|" - { d'4 ~ d'8[ ais8^\markup { \pad-markup #0.2 "+35"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }}] ~ ais2 ~ } - \bar "|" \break - { ais1 ~ } - \bar "|" - { ais8.[ b16^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↓" }}] ~ b2. ~ } - \bar "|" - { b1 ~ } - \bar "|" - { b1 ~ } - \bar "|" \break - { b1 ~ } - \bar "|" - { b1 ~ } - \bar "|" - { b1 ~ } - \bar "|" - { b1 ~ } - \bar "|" \break - { b2 ~ b16[ r8.] r4 } - \bar "|" - { r1 } - \bar "|" - { r1 } -\bar "||" -} \ No newline at end of file +\include "../../resources/4a8a6e53/lilypond/part_I.ly" +\include "../../resources/66f6a618/lilypond/part_I.ly" +\include "../../resources/490b1e6e/lilypond/part_I.ly" +\include "../../resources/46985d14/lilypond/part_I.ly" +\include "../../resources/761e4585/lilypond/part_I.ly" +\include "../../resources/7ebbb471/lilypond/part_I.ly" diff --git a/lilypond/includes/part_II.ly b/lilypond/includes/part_II.ly index e099765..6372dac 100644 --- a/lilypond/includes/part_II.ly +++ b/lilypond/includes/part_II.ly @@ -1,56 +1,6 @@ -{ - { r1 } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" \break - { r1 } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" - { r16[ ais'8.^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ ais'2. ~ } - \bar "|" \break - { ais'2. gis'4^\markup { \pad-markup #0.2 "+14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 10↓" }} ~ } - \bar "|" - { gis'16[ fis'8.^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 14↓" }}] ~ fis'2. ~ } - \bar "|" - { fis'1 ~ } - \bar "|" - { fis'1 ~ } - \bar "|" \break - { fis'1 ~ } - \bar "|" - { fis'1 ~ } - \bar "|" - { fis'1 ~ } - \bar "|" - { fis'1 ~ } - \bar "|" \break - { fis'1 ~ } - \bar "|" - { fis'2 ~ fis'8.[ r16] r4 } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" \break - { r1 } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" - { r1 } - \bar "|" \break - { r1 } - \bar "|" - { r1 } - \bar "|" - { r1 } -\bar "||" -} \ No newline at end of file +\include "../../resources/4a8a6e53/lilypond/part_II.ly" +\include "../../resources/66f6a618/lilypond/part_II.ly" +\include "../../resources/490b1e6e/lilypond/part_II.ly" +\include "../../resources/46985d14/lilypond/part_II.ly" +\include "../../resources/761e4585/lilypond/part_II.ly" +\include "../../resources/7ebbb471/lilypond/part_II.ly" diff --git a/lilypond/includes/part_III.ly b/lilypond/includes/part_III.ly index 1830d66..c1b00f8 100644 --- a/lilypond/includes/part_III.ly +++ b/lilypond/includes/part_III.ly @@ -1,56 +1,6 @@ -{ - { r4 r8.[ g16^\markup { \pad-markup #0.2 "+2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ g2 ~ } - \bar "|" - { g1 ~ } - \bar "|" - { g1 ~ } - \bar "|" - { g1 ~ } - \bar "|" \break - { g1 ~ } - \bar "|" - { g2. ~ g8[ ais8^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↑" }}] ~ } - \bar "|" - { ais1 ~ } - \bar "|" - { ais1 ~ } - \bar "|" \break - { ais1 ~ } - \bar "|" - { ais1 ~ } - \bar "|" - { ais1 ~ } - \bar "|" - { ais1 } - \bar "|" \break - { b4^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↓" }} ~ b2. ~ } - \bar "|" - { b1 ~ } - \bar "|" - { b4 ~ b8[ ais8^\markup { \pad-markup #0.2 "+35"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }}] ~ ais8.[ a16^\markup { \pad-markup #0.2 "-20"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↓" }}] ~ a4 ~ } - \bar "|" - { a1 ~ } - \bar "|" \break - { a1 ~ } - \bar "|" - { a1 ~ } - \bar "|" - { a16[ a8.^\markup { \pad-markup #0.2 "+16"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 7↑" }}] ~ a2. } - \bar "|" - { gis4^\markup { \pad-markup #0.2 "-13"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↑" }} ~ gis2. ~ } - \bar "|" \break - { gis8.[ gis16^\markup { \pad-markup #0.2 "-40"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↓" }}] ~ gis2. ~ } - \bar "|" - { gis4 ~ gis8[ f8^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }}] ~ f4 fis4^\markup { \pad-markup #0.2 "-5"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↓" }} ~ } - \bar "|" - { fis2. ~ fis8[ gis8^\markup { \pad-markup #0.2 "-13"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↑" }}] ~ } - \bar "|" - { gis1 ~ } - \bar "|" \break - { gis1 ~ } - \bar "|" - { gis1 } - \bar "|" - { r1 } -\bar "||" -} \ No newline at end of file +\include "../../resources/4a8a6e53/lilypond/part_III.ly" +\include "../../resources/66f6a618/lilypond/part_III.ly" +\include "../../resources/490b1e6e/lilypond/part_III.ly" +\include "../../resources/46985d14/lilypond/part_III.ly" +\include "../../resources/761e4585/lilypond/part_III.ly" +\include "../../resources/7ebbb471/lilypond/part_III.ly" diff --git a/lilypond/includes/part_IV.ly b/lilypond/includes/part_IV.ly index 15b5e72..9de4b95 100644 --- a/lilypond/includes/part_IV.ly +++ b/lilypond/includes/part_IV.ly @@ -1,56 +1,6 @@ -{ - { c,4^\markup { \pad-markup #0.2 "+0"} ~ c,2. ~ } - \bar "|" - { c,1 ~ } - \bar "|" - { c,1 ~ } - \bar "|" - { c,1 ~ } - \bar "|" \break - { c,1 ~ } - \bar "|" - { c,1 ~ } - \bar "|" - { c,4 ~ c,16[ e,8.^\markup { \pad-markup #0.2 "-41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 14↓" }}] ~ e,2 ~ } - \bar "|" - { e,2 ~ e,8.[ g,16^\markup { \pad-markup #0.2 "+2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ g,4 ~ } - \bar "|" \break - { g,4 ~ g,8[ ais,8^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ ais,2 ~ } - \bar "|" - { ais,4 ~ ais,8.[ gis,16^\markup { \pad-markup #0.2 "+41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ gis,2 } - \bar "|" - { gis,4^\markup { \pad-markup #0.2 "+14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 10↓" }} ~ gis,4 ~ gis,8[ g,8^\markup { \pad-markup #0.2 "+2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ g,4 ~ } - \bar "|" - { g,8[ f,8^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 6↓" }}] ~ f,2. ~ } - \bar "|" \break - { f,4 ~ f,8[ e,8^\markup { \pad-markup #0.2 "+18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↑" }}] ~ e,2 ~ } - \bar "|" - { e,1 ~ } - \bar "|" - { e,16[ fis,8.^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 1↑" }}] ~ fis,2. ~ } - \bar "|" - { fis,1 ~ } - \bar "|" \break - { fis,16[ a,8.^\markup { \pad-markup #0.2 "-20"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ a,2 ~ a,16[ ais,8.^\markup { \pad-markup #0.2 "+8"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↓" }}] ~ } - \bar "|" - { ais,1 ~ } - \bar "|" - { ais,2 ~ ais,16[ b,8.^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 1↑" }}] ~ b,4 ~ } - \bar "|" - { b,2 ~ b,8[ a,8^\markup { \pad-markup #0.2 "+16"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 7↑" }}] ~ a,4 ~ } - \bar "|" \break - { a,2 ~ a,8.[ fis,16^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↑" }}] ~ fis,4 ~ } - \bar "|" - { fis,16[ f,8.^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↑" }}] ~ f,2. ~ } - \bar "|" - { f,4 ~ f,8.[ e,16^\markup { \pad-markup #0.2 "+45"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↓" }}] ~ e,2 ~ } - \bar "|" - { e,1 ~ } - \bar "|" \break - { e,1 ~ } - \bar "|" - { e,4 ~ e,16[ r8.] r2 } - \bar "|" - { r1 } -\bar "||" -} \ No newline at end of file +\include "../../resources/4a8a6e53/lilypond/part_IV.ly" +\include "../../resources/66f6a618/lilypond/part_IV.ly" +\include "../../resources/490b1e6e/lilypond/part_IV.ly" +\include "../../resources/46985d14/lilypond/part_IV.ly" +\include "../../resources/761e4585/lilypond/part_IV.ly" +\include "../../resources/7ebbb471/lilypond/part_IV.ly" diff --git a/lilypond/score_template.pdf b/lilypond/score_template.pdf index ec5770cdc10cd404d2a3d0b33cf1a0c1f897b0a4..b6cd406f1c8ec53967f0a3325a1770f7ce166aff 100644 GIT binary patch literal 806447 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

|A(Y5pT+5?U3HX6A5vYl_kzFNoVUX6%kq3% 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


nec5NDO^k$kGf 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 F{|5;yakT&d delta 63256 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%0D+*+}8X7zr?68{M_cR&KE=>gYmi7kS$Or=U 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 diff --git a/open_stage_control/seeds_and_ledgers_mixer.json b/open_stage_control/seeds_and_ledgers_mixer.json index b68f9f7..9fdf58a 100644 --- a/open_stage_control/seeds_and_ledgers_mixer.json +++ b/open_stage_control/seeds_and_ledgers_mixer.json @@ -563,364 +563,6 @@ "onValue": "", "widgets": [], "tabs": [] - }, - { - "type": "panel", - "top": 320, - "left": 0, - "lock": false, - "id": "mixer/bass/panel", - "visible": true, - "interaction": true, - "comments": "", - "width": 280, - "height": 40, - "expand": "false", - "colorText": "auto", - "colorWidget": "auto", - "colorStroke": "auto", - "colorFill": "auto", - "alphaStroke": "auto", - "alphaFillOff": "auto", - "alphaFillOn": "auto", - "lineWidth": "auto", - "borderRadius": "auto", - "padding": "auto", - "html": "", - "css": "", - "colorBg": "auto", - "layout": "default", - "justify": "start", - "gridTemplate": "", - "contain": true, - "scroll": true, - "innerPadding": true, - "tabsPosition": "top", - "variables": "@{parent.variables}", - "traversing": false, - "value": "", - "default": "", - "linkId": "", - "address": "auto", - "preArgs": "", - "typeTags": "", - "decimals": 2, - "target": "", - "ignoreDefaults": false, - "bypass": false, - "onCreate": "", - "onValue": "", - "widgets": [ - { - "type": "fader", - "top": 0, - "left": 0, - "lock": false, - "id": "mixer/bass/volume/master", - "visible": true, - "interaction": true, - "comments": "", - "width": 270, - "height": 30, - "expand": "false", - "colorText": "auto", - "colorWidget": "auto", - "colorStroke": "auto", - "colorFill": "auto", - "alphaStroke": "auto", - "alphaFillOff": "auto", - "alphaFillOn": "auto", - "lineWidth": "auto", - "borderRadius": "auto", - "padding": "auto", - "html": "", - "css": "", - "design": "default", - "knobSize": "auto", - "horizontal": true, - "pips": false, - "dashed": false, - "gradient": [], - "snap": false, - "spring": false, - "doubleTap": false, - "range": { - "min": 0, - "max": 1 - }, - "logScale": false, - "sensitivity": 1, - "steps": "", - "origin": "auto", - "value": "", - "default": "", - "linkId": "", - "address": "auto", - "preArgs": "", - "typeTags": "", - "decimals": 2, - "target": "", - "ignoreDefaults": false, - "bypass": false, - "onCreate": "", - "onValue": "", - "onTouch": "" - } - ], - "tabs": [] - }, - { - "type": "panel", - "top": 320, - "left": 340, - "lock": false, - "id": "mixer/hdust/panel", - "visible": true, - "interaction": true, - "comments": "", - "width": 280, - "height": 40, - "expand": "false", - "colorText": "auto", - "colorWidget": "auto", - "colorStroke": "auto", - "colorFill": "auto", - "alphaStroke": "auto", - "alphaFillOff": "auto", - "alphaFillOn": "auto", - "lineWidth": "auto", - "borderRadius": "auto", - "padding": "auto", - "html": "", - "css": "", - "colorBg": "auto", - "layout": "default", - "justify": "start", - "gridTemplate": "", - "contain": true, - "scroll": true, - "innerPadding": true, - "tabsPosition": "top", - "variables": "@{parent.variables}", - "traversing": false, - "value": "", - "default": "", - "linkId": "", - "address": "auto", - "preArgs": "", - "typeTags": "", - "decimals": 2, - "target": "", - "ignoreDefaults": false, - "bypass": false, - "onCreate": "", - "onValue": "", - "widgets": [ - { - "type": "fader", - "top": 0, - "left": 0, - "lock": false, - "id": "mixer/hdust/volume/master", - "visible": true, - "interaction": true, - "comments": "", - "width": 270, - "height": 30, - "expand": "false", - "colorText": "auto", - "colorWidget": "auto", - "colorStroke": "auto", - "colorFill": "auto", - "alphaStroke": "auto", - "alphaFillOff": "auto", - "alphaFillOn": "auto", - "lineWidth": "auto", - "borderRadius": "auto", - "padding": "auto", - "html": "", - "css": "", - "design": "default", - "knobSize": "auto", - "horizontal": true, - "pips": false, - "dashed": false, - "gradient": [], - "snap": false, - "spring": false, - "doubleTap": false, - "range": { - "min": 0, - "max": 1 - }, - "logScale": false, - "sensitivity": 1, - "steps": "", - "origin": "auto", - "value": "", - "default": "", - "linkId": "", - "address": "auto", - "preArgs": "", - "typeTags": "", - "decimals": 2, - "target": "", - "ignoreDefaults": false, - "bypass": false, - "onCreate": "", - "onValue": "", - "onTouch": "" - } - ], - "tabs": [] - }, - { - "type": "file", - "top": 380, - "left": 290, - "lock": false, - "id": "sampler", - "visible": true, - "interaction": true, - "comments": "", - "width": 120, - "height": 40, - "expand": "false", - "colorText": "auto", - "colorWidget": "auto", - "colorStroke": "auto", - "colorFill": "auto", - "alphaStroke": "auto", - "alphaFillOff": "auto", - "alphaFillOn": "auto", - "lineWidth": "auto", - "borderRadius": "auto", - "padding": "auto", - "html": "", - "css": "", - "align": "center", - "hidePath": true, - "mode": "open", - "directory": "/home/mwinter/Sketches/seeds_and_ledgers/alot_recs/", - "extension": "*", - "allowDir": false, - "value": "", - "default": "", - "linkId": "", - "address": "auto", - "preArgs": "", - "typeTags": "", - "decimals": 2, - "target": "", - "ignoreDefaults": false, - "bypass": false, - "onCreate": "", - "onValue": "" - }, - { - "type": "panel", - "top": 380, - "left": 0, - "lock": false, - "id": "mixer/sampler/panel", - "visible": true, - "interaction": true, - "comments": "", - "width": 280, - "height": 40, - "expand": "false", - "colorText": "auto", - "colorWidget": "auto", - "colorStroke": "auto", - "colorFill": "auto", - "alphaStroke": "auto", - "alphaFillOff": "auto", - "alphaFillOn": "auto", - "lineWidth": "auto", - "borderRadius": "auto", - "padding": "auto", - "html": "", - "css": "", - "colorBg": "auto", - "layout": "default", - "justify": "start", - "gridTemplate": "", - "contain": true, - "scroll": true, - "innerPadding": true, - "tabsPosition": "top", - "variables": "@{parent.variables}", - "traversing": false, - "value": "", - "default": "", - "linkId": "", - "address": "auto", - "preArgs": "", - "typeTags": "", - "decimals": 2, - "target": "", - "ignoreDefaults": false, - "bypass": false, - "onCreate": "", - "onValue": "", - "widgets": [ - { - "type": "fader", - "top": 0, - "left": 0, - "lock": false, - "id": "mixer/sampler/volume/master", - "visible": true, - "interaction": true, - "comments": "", - "width": 270, - "height": 30, - "expand": "false", - "colorText": "auto", - "colorWidget": "auto", - "colorStroke": "auto", - "colorFill": "auto", - "alphaStroke": "auto", - "alphaFillOff": "auto", - "alphaFillOn": "auto", - "lineWidth": "auto", - "borderRadius": "auto", - "padding": "auto", - "html": "", - "css": "", - "design": "default", - "knobSize": "auto", - "horizontal": true, - "pips": false, - "dashed": false, - "gradient": [], - "snap": false, - "spring": false, - "doubleTap": false, - "range": { - "min": 0, - "max": 1 - }, - "logScale": false, - "sensitivity": 1, - "steps": "", - "origin": "auto", - "value": "", - "default": "", - "linkId": "", - "address": "auto", - "preArgs": "", - "typeTags": "", - "decimals": 2, - "target": "", - "ignoreDefaults": false, - "bypass": false, - "onCreate": "", - "onValue": "", - "onTouch": "" - } - ], - "tabs": [] } ], "tabs": [] diff --git a/resources/445b7057/445b7057_code.scd b/resources/445b7057/445b7057_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/445b7057/445b7057_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/445b7057/445b7057_mus_model.json b/resources/445b7057/445b7057_mus_model.json new file mode 100644 index 0000000..4e00d32 --- /dev/null +++ b/resources/445b7057/445b7057_mus_model.json @@ -0,0 +1,97 @@ +{ +"music_data": +[ + [ + [ + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.625 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ] ], 1.5 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.75 ] + ], + [ + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.5 ], + [ [ [ -1, 0, 0, 1, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ -1, 0, 0, 1, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.375 ], + [ [ [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.75 ], + [ [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.125 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, -1, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ] + ], + [ + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, -1, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, -1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 1, 0, -1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1.375 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1.375 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 2, 0, 0, -1, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 0, 0, 1, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1 ] + ], + [ + [ [ [ "Rest" ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 0.75 ], + [ [ [ "Rest" ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 1, -1, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 2, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 1, -1, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 2, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 1 ] ], 1.25 ], + [ [ [ "Rest" ], [ 1, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 1 ] ], 1.125 ], + [ [ [ "Rest" ], [ 1, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 1, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 0 ] ], 0.75 ], + [ [ [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 0 ] ], 0.625 ], + [ [ [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.75 ], + [ [ [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -2, 0 ] ], 1.375 ], + [ [ [ "Rest" ], [ 2, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -2, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 2, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 1 ] ], 3.375 ], + [ [ [ "Rest" ], [ 2, -2, 0, 0, -1, 0 ], [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ] ], 1.5 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ] ], 1.375 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 1, 0, 0, 0, 0, -2 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 1, 0, -1 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ] +], +"cur_uid": "445b7057", +"ref_uid": "nil", +"order_seed": 320463, +"dur_seed": 903977, +"motifs_seed": 895384, +"entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], +"passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 1 ], [ 0, 3, 2, 3, 3, 2 ], [ ] ], + [ [ 2, 1 ], [ 3, 0, 3 ], [ ] ], + [ [ 1 ], [ 3, 2, 0 ], [ ] ] +], +"sus_weights": [ 0.75, 0.69, 0.75 ], +"order_size": [ 2, 6 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/46985d14/46985d14_code.scd b/resources/46985d14/46985d14_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/46985d14/46985d14_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/46985d14/46985d14_mus_model.json b/resources/46985d14/46985d14_mus_model.json new file mode 100644 index 0000000..0e5228e --- /dev/null +++ b/resources/46985d14/46985d14_mus_model.json @@ -0,0 +1,86 @@ +{ +"music_data": +[ + [ + [ + [ [ [ "Rest" ], [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ "Rest" ] ], 1.75 ], + [ [ [ -1, -1, 0, 1, 1, 0 ], [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ "Rest" ] ], 0 ], + [ [ [ -1, -1, 0, 1, 1, 0 ], [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ 0, -1, 1, 0, 1, 0 ] ], 0 ], + [ [ [ -1, -1, 0, 1, 1, 0 ], [ -1, -1, 0, 0, 1, 1 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, -1, 1, 0, 1, 0 ] ], 1.25 ], + [ [ [ 0, -1, -1, 0, 1, 0 ], [ -1, -1, 0, 0, 1, 1 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, -1, 1, 0, 1, 0 ] ], 1.75 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -1, -1, 0, 0, 1, 1 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, -1, 1, 0, 1, 0 ] ], 0 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -1, -1, 0, 0, 1, 1 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -2, 0, 0, 1, 0 ] ], 0 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -1, -1, 0, 1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -2, 0, 0, 1, 0 ] ], 0.875 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -2, 0, 0, 1, 0 ] ], 0 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, 0, 1, -1 ] ], 1.625 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 1, -2, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, 0, 1, -1 ] ], 1.75 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, 0, 1, -1 ] ], 0 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.875 ] + ], + [ + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ -1, 0, 1, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.625 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.625 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ -1, 0, 0, 0, 2, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.125 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 1, -1, -1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.5 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, -1, 1, 1 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.625 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.5 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.5 ] + ], + [ + [ [ [ 1, -2, 0, -1, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.5 ], + [ [ [ 0, 0, 0, -1, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.5 ], + [ [ [ -1, 0, 0, 1, 1, -1 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.125 ] + ], + [ + [ [ [ 1, -1, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, -1 ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ "Rest" ] ], 1.375 ] + ], + [ + [ [ [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.75 ], + [ [ [ 0, -1, 0, -1, 2, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.875 ], + [ [ [ -1, -1, 0, 0, 1, 1 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.75 ], + [ [ [ 0, -1, -1, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.25 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1 ], + [ [ [ 1, -2, 0, -1, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.375 ], + [ [ [ 1, -1, 0, -1, 0, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 2 ], + [ [ [ 1, -1, 0, -1, 0, 0 ], [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 0, 0 ], [ "Rest" ], [ "Rest" ], [ 1, -1, 0, -1, 1, 0 ] ], 1.75 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ 1, -1, 0, -1, 1, 0 ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "46985d14", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/46985d14/lilypond/part_I.ly b/resources/46985d14/lilypond/part_I.ly new file mode 100644 index 0000000..ffbab4a --- /dev/null +++ b/resources/46985d14/lilypond/part_I.ly @@ -0,0 +1,40 @@ +{ + { r2. r8[ d'8^\markup { \pad-markup #0.2 "+36"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↑" }}] ~ } + \bar "|" + { d'1 ~ } + \bar "|" + { d'4 ~ d'8[ dis'8^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↓" }}] ~ dis'4 ~ dis'16[ d'8.^\markup { \pad-markup #0.2 "+9"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↓" }}] ~ } + \bar "|" + { d'1 ~ } + \bar "|" \break + { d'2 cis'4^\markup { \pad-markup #0.2 "-19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↓" }} ~ cis'4 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'2 r2 } + \bar "|" \break + { r8.[ cis'16^\markup { \pad-markup #0.2 "-19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↓" }}] ~ cis'2. ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'16[ r8.] r2. } +\bar "||" +} \ No newline at end of file diff --git a/resources/46985d14/lilypond/part_II.ly b/resources/46985d14/lilypond/part_II.ly new file mode 100644 index 0000000..e79b14a --- /dev/null +++ b/resources/46985d14/lilypond/part_II.ly @@ -0,0 +1,40 @@ +{ + { ais4^\markup { \pad-markup #0.2 "+49"} ~ ais2. ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" \break + { ais2. ~ ais8.[ a16^\markup { \pad-markup #0.2 "+38"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }}] ~ } + \bar "|" + { a4 a4^\markup { \pad-markup #0.2 "+11"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↓" }} ~ a2 ~ } + \bar "|" + { a16[ b8.^\markup { \pad-markup #0.2 "+3"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↑" }}] ~ b4 ~ b8[ a8^\markup { \pad-markup #0.2 "-6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↓" }}] ~ a4 ~ } + \bar "|" + { a8[ gis8^\markup { \pad-markup #0.2 "-18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↓" }}] ~ gis8[ a8^\markup { \pad-markup #0.2 "+21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↑" }}] ~ a2 ~ } + \bar "|" \break + { a8.[ ais16^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ ais2 ~ ais8.[ a16^\markup { \pad-markup #0.2 "+11"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↓" }}] ~ } + \bar "|" + { a1 ~ } + \bar "|" + { a1 ~ } + \bar "|" + { a2 ais4^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↓" }} ~ ais4 ~ } + \bar "|" \break + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" \break + { ais1 ~ } + \bar "|" + { ais8.[ r16] r2. } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/46985d14/lilypond/part_III.ly b/resources/46985d14/lilypond/part_III.ly new file mode 100644 index 0000000..150adb9 --- /dev/null +++ b/resources/46985d14/lilypond/part_III.ly @@ -0,0 +1,40 @@ +{ + { r2. r8[ g8^\markup { \pad-markup #0.2 "-10"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↑" }}] ~ } + \bar "|" + { g1 ~ } + \bar "|" + { g4 ~ g8[ gis8^\markup { \pad-markup #0.2 "+18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↑" }}] ~ gis4 ~ gis16[ cis'8.^\markup { \pad-markup #0.2 "-19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↓" }}] ~ } + \bar "|" + { cis'2 ~ cis'8[ dis'8^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↓" }}] ~ dis'4 ~ } + \bar "|" \break + { dis'2 fis'4^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }} ~ fis'4 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'8.[ r16] r2. } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/46985d14/lilypond/part_IV.ly b/resources/46985d14/lilypond/part_IV.ly new file mode 100644 index 0000000..1cb083b --- /dev/null +++ b/resources/46985d14/lilypond/part_IV.ly @@ -0,0 +1,40 @@ +{ + { r2. r8[ gis8^\markup { \pad-markup #0.2 "+18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↑" }}] ~ } + \bar "|" + { gis2 g2^\markup { \pad-markup #0.2 "-37"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↓" }} ~ } + \bar "|" + { g4 ~ g8[ fis8^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↑" }}] ~ fis2 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis8.[ fis16^\markup { \pad-markup #0.2 "-21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↓" }}] ~ fis8.[ gis16^\markup { \pad-markup #0.2 "-18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 7↓" }}] ~ gis2 ~ } + \bar "|" + { gis8.[ g16^\markup { \pad-markup #0.2 "-20"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↑" }}] ~ g2 ~ g8.[ g16^\markup { \pad-markup #0.2 "+29"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↓" }}] ~ } + \bar "|" + { g2 r2 } + \bar "|" \break + { r1 } + \bar "|" + { r16[ fis8.^\markup { \pad-markup #0.2 "+32"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↑" }}] ~ fis2. } + \bar "|" + { g4^\markup { \pad-markup #0.2 "-10"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↑" }} ~ g8[ g8^\markup { \pad-markup #0.2 "-37"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↓" }}] ~ g2 } + \bar "|" + { fis4^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }} ~ fis4 fis2^\markup { \pad-markup #0.2 "-21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↓" }} ~ } + \bar "|" \break + { fis8.[ g16^\markup { \pad-markup #0.2 "+29"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↓" }}] ~ g2. ~ } + \bar "|" + { g1 ~ } + \bar "|" + { g16[ r8.] r2. } +\bar "||" +} \ No newline at end of file diff --git a/resources/490b1e6e/490b1e6e_code.scd b/resources/490b1e6e/490b1e6e_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/490b1e6e/490b1e6e_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/490b1e6e/490b1e6e_mus_model.json b/resources/490b1e6e/490b1e6e_mus_model.json new file mode 100644 index 0000000..64ab1de --- /dev/null +++ b/resources/490b1e6e/490b1e6e_mus_model.json @@ -0,0 +1,63 @@ +{ +"music_data": +[ + [ + [ + [ [ [ "Rest" ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 1.375 ], + [ [ [ -2, 0, 0, 1, 1, 1 ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0.875 ], + [ [ [ -2, 1, 0, 1, 1, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0.875 ], + [ [ [ -2, 0, 0, 1, 2, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0.75 ], + [ [ [ -1, 0, 0, 1, 0, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 1.5 ], + [ [ [ -1, -1, 0, 1, 1, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 1.125 ], + [ [ [ -2, 0, 1, 1, 1, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 1.625 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 1.625 ] + ], + [ + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ "Rest" ] ], 0 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -2, 0, 0, 1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.25 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -1, -1, 1, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.125 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, -2, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -2, 1, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 0.5 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -1, 0, -1, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.625 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, -1 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 2 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, -1, 0, -1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.75 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ -1, -1, 1, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.5 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, -2, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.25 ], + [ [ [ -1, 0, 0, 0, 1, 0 ], [ 0, -2, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ "Rest" ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, -2, 0, 0, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ 0, -2, 0, 0, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1.375 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "490b1e6e", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/490b1e6e/lilypond/part_I.ly b/resources/490b1e6e/lilypond/part_I.ly new file mode 100644 index 0000000..d4e584d --- /dev/null +++ b/resources/490b1e6e/lilypond/part_I.ly @@ -0,0 +1,28 @@ +{ + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r2. r8[ fis'8^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }}] ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'2. ~ fis'8[ r8] } + \bar "|" + { r1 } + \bar "|" \break + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/490b1e6e/lilypond/part_II.ly b/resources/490b1e6e/lilypond/part_II.ly new file mode 100644 index 0000000..d155da0 --- /dev/null +++ b/resources/490b1e6e/lilypond/part_II.ly @@ -0,0 +1,28 @@ +{ + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r2. r8[ ais8^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" \break + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais4 ~ ais16[ r8.] r2 } + \bar "|" \break + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/490b1e6e/lilypond/part_III.ly b/resources/490b1e6e/lilypond/part_III.ly new file mode 100644 index 0000000..001dc14 --- /dev/null +++ b/resources/490b1e6e/lilypond/part_III.ly @@ -0,0 +1,28 @@ +{ + { dis4^\markup { \pad-markup #0.2 "+20"} ~ dis2. ~ } + \bar "|" + { dis1 ~ } + \bar "|" + { dis1 ~ } + \bar "|" + { dis1 ~ } + \bar "|" \break + { dis1 ~ } + \bar "|" + { dis2 d4^\markup { \pad-markup #0.2 "+36"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↑" }} ~ d4 ~ } + \bar "|" + { d16[ dis8.^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↓" }}] ~ dis4 ~ dis16[ cis8.^\markup { \pad-markup #0.2 "-47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ cis16[ d8.^\markup { \pad-markup #0.2 "-35"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↓" }}] ~ } + \bar "|" + { d2 ~ d8[ d8^\markup { \pad-markup #0.2 "+9"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↓" }}] ~ d4 ~ } + \bar "|" \break + { d2 ~ d8[ cis8^\markup { \pad-markup #0.2 "-19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↓" }}] ~ cis4 ~ } + \bar "|" + { cis2 d4^\markup { \pad-markup #0.2 "+36"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↑" }} ~ d4 ~ } + \bar "|" + { d4 dis4^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↓" }} ~ dis2 ~ } + \bar "|" + { dis4 ~ dis16[ r8.] r2 } + \bar "|" \break + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/490b1e6e/lilypond/part_IV.ly b/resources/490b1e6e/lilypond/part_IV.ly new file mode 100644 index 0000000..720aad3 --- /dev/null +++ b/resources/490b1e6e/lilypond/part_IV.ly @@ -0,0 +1,28 @@ +{ + { r2 r8.[ c'16^\markup { \pad-markup #0.2 "-39"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 13↑" }}] ~ c'4 ~ } + \bar "|" + { c'8[ ais8^\markup { \pad-markup #0.2 "+22"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↑" }}] ~ ais4 ~ ais16[ a8.^\markup { \pad-markup #0.2 "-29"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 11↑" }}] ~ a8.[ ais16^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 11↓" }}] ~ } + \bar "|" + { ais2 ~ ais8.[ gis16^\markup { \pad-markup #0.2 "+18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↓" }}] ~ gis4 ~ } + \bar "|" + { gis4 g2.^\markup { \pad-markup #0.2 "+6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 5↑" }} ~ } + \bar "|" \break + { g16[ fis8.^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 7↓" }}] ~ fis2. ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis4 ~ fis16[ r8.] r2 } + \bar "|" \break + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/4a8a6e53/4a8a6e53_code.scd b/resources/4a8a6e53/4a8a6e53_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/4a8a6e53/4a8a6e53_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/4a8a6e53/4a8a6e53_mus_model.json b/resources/4a8a6e53/4a8a6e53_mus_model.json new file mode 100644 index 0000000..715f342 --- /dev/null +++ b/resources/4a8a6e53/4a8a6e53_mus_model.json @@ -0,0 +1,97 @@ +{ +"music_data": +[ + [ + [ + [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ] ], 1 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, -1, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ] ], 0.75 ] + ], + [ + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ "Rest" ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 1, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ "Rest" ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 1, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ] ], 1.375 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 1.125 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, -1, 0, 0, 0 ], [ "Rest" ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ] ], 1 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ] ], 0.875 ] + ], + [ + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, -1, 0, -1, 0 ] ], 0.875 ], + [ [ [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, -1, 0, -1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 1, 0, -1, 0, -1, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 0, 0, 0, 0, -1, 1 ] ], 1.25 ], + [ [ [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, 0, 1, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 1, 0, 0, -1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 1.375 ], + [ [ [ 1, 0, 0, -1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ] ], 1.375 ], + [ [ [ 1, 0, 0, -1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 2, 0, 0, -1, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ] ], 1.5 ], + [ [ [ 1, 0, 0, -1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 0, 0, 1, 0, -1, 0 ] ], 0.75 ], + [ [ [ 1, 0, 0, -1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1 ] + ], + [ + [ [ [ 1, 0, 0, -1, -1, 0 ], [ "Rest" ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ 0, -1, 0, 1, -1, 0 ], [ "Rest" ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1 ], + [ [ [ 0, -1, 0, 1, -1, 0 ], [ "Rest" ], [ 2, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 2, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.25 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 1, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.125 ], + [ [ [ 1, -1, -1, 0, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1 ], + [ [ [ 1, -1, -1, 0, -1, 0 ], [ "Rest" ], [ 1, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ 1, -1, -1, 0, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, -2, 0 ], [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.375 ], + [ [ [ 1, -1, 0, 0, -2, 0 ], [ "Rest" ], [ 2, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 2, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 3.375 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 2, -2, 0, 0, -1, 0 ], [ "Rest" ] ], 1.5 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1.375 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 1, 0, 0, 0, 0, -2 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 1, 0, -1 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ] +], +"cur_uid": "4a8a6e53", +"ref_uid": "nil", +"order_seed": 320463, +"dur_seed": 903977, +"motifs_seed": 895384, +"entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], +"passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 1 ], [ 0, 3, 2, 3, 3, 2 ], [ ] ], + [ [ 2, 1 ], [ 3, 0, 3 ], [ ] ], + [ [ 1 ], [ 3, 2, 0 ], [ ] ] +], +"sus_weights": [ 0.75, 0.69, 0.75 ], +"order_size": [ 2, 6 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/4a8a6e53/lilypond/part_I.ly b/resources/4a8a6e53/lilypond/part_I.ly new file mode 100644 index 0000000..d4a6b53 --- /dev/null +++ b/resources/4a8a6e53/lilypond/part_I.ly @@ -0,0 +1,56 @@ +{ + { r2. e'4^\markup { \pad-markup #0.2 "-41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↓" }} ~ } + \bar "|" + { e'2 e'4^\markup { \pad-markup #0.2 "-14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }} ~ e'8.[ f'16^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ } + \bar "|" + { f'4 ~ f'8[ g'8^\markup { \pad-markup #0.2 "+2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ g'4 ~ g'8[ ais'8^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↑" }}] ~ } + \bar "|" + { ais'4 ~ ais'8[ c''8^\markup { \pad-markup #0.2 "+0"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }}] ~ c''2 } + \bar "|" \break + { gis'4^\markup { \pad-markup #0.2 "+41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↑" }} ~ gis'8.[ gis'16^\markup { \pad-markup #0.2 "+14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↓" }}] ~ gis'4 ~ gis'8[ f'8^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ } + \bar "|" + { f'2 e'4^\markup { \pad-markup #0.2 "-14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }} ~ e'8[ r8] } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r2 r16[ dis'8.^\markup { \pad-markup #0.2 "-38"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 5↓" }}] ~ dis'4 ~ } + \bar "|" \break + { dis'1 ~ } + \bar "|" + { dis'8[ dis'8^\markup { \pad-markup #0.2 "-11"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 13↑" }}] ~ dis'2 d'4^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↑" }} ~ } + \bar "|" + { d'1 ~ } + \bar "|" + { d'4 ~ d'8[ ais8^\markup { \pad-markup #0.2 "+35"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 5↑" }}] ~ ais2 ~ } + \bar "|" \break + { ais1 ~ } + \bar "|" + { ais8.[ b16^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↓" }}] ~ b2. ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b2 ~ b16[ r8.] r4 } + \bar "|" + { r1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/4a8a6e53/lilypond/part_II.ly b/resources/4a8a6e53/lilypond/part_II.ly new file mode 100644 index 0000000..21ca368 --- /dev/null +++ b/resources/4a8a6e53/lilypond/part_II.ly @@ -0,0 +1,56 @@ +{ + { c'4^\markup { \pad-markup #0.2 "+0"} ~ c'2. ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" \break + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'4 ~ c'16[ e'8.^\markup { \pad-markup #0.2 "-41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↓" }}] ~ e'2 ~ } + \bar "|" + { e'2 ~ e'8.[ g'16^\markup { \pad-markup #0.2 "+2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ g'4 ~ } + \bar "|" \break + { g'4 ~ g'8[ ais'8^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ ais'2 ~ } + \bar "|" + { ais'4 ~ ais'8.[ gis'16^\markup { \pad-markup #0.2 "+41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↑" }}] ~ gis'2 } + \bar "|" + { gis'4^\markup { \pad-markup #0.2 "+14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↓" }} ~ gis'4 ~ gis'8[ g'8^\markup { \pad-markup #0.2 "+2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ g'4 ~ } + \bar "|" + { g'8[ f'8^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ f'2. ~ } + \bar "|" \break + { f'4 ~ f'8[ e'8^\markup { \pad-markup #0.2 "+18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 7↑" }}] ~ e'2 ~ } + \bar "|" + { e'1 ~ } + \bar "|" + { e'16[ fis'8.^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ fis'2. ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'16[ a'8.^\markup { \pad-markup #0.2 "-20"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }}] ~ a'2 ~ a'16[ ais'8.^\markup { \pad-markup #0.2 "+8"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 13↓" }}] ~ } + \bar "|" + { ais'1 ~ } + \bar "|" + { ais'2 ~ ais'16[ b'8.^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 1↑" }}] ~ b'4 ~ } + \bar "|" + { b'2 ~ b'8[ a'8^\markup { \pad-markup #0.2 "+16"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 7↑" }}] ~ a'4 ~ } + \bar "|" \break + { a'2 ~ a'8.[ fis'16^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↑" }}] ~ fis'4 ~ } + \bar "|" + { fis'16[ f'8.^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↑" }}] ~ f'2. ~ } + \bar "|" + { f'4 ~ f'8.[ e'16^\markup { \pad-markup #0.2 "+45"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↓" }}] ~ e'2 ~ } + \bar "|" + { e'1 ~ } + \bar "|" \break + { e'1 ~ } + \bar "|" + { e'4 ~ e'16[ r8.] r2 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/4a8a6e53/lilypond/part_III.ly b/resources/4a8a6e53/lilypond/part_III.ly new file mode 100644 index 0000000..f792fe6 --- /dev/null +++ b/resources/4a8a6e53/lilypond/part_III.ly @@ -0,0 +1,56 @@ +{ + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r16[ ais8.^\markup { \pad-markup #0.2 "-31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↑" }}] ~ ais2. ~ } + \bar "|" \break + { ais2. gis4^\markup { \pad-markup #0.2 "+14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↓" }} ~ } + \bar "|" + { gis16[ fis8.^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↓" }}] ~ fis2. ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis2 ~ fis8.[ r16] r4 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/4a8a6e53/lilypond/part_IV.ly b/resources/4a8a6e53/lilypond/part_IV.ly new file mode 100644 index 0000000..17f6594 --- /dev/null +++ b/resources/4a8a6e53/lilypond/part_IV.ly @@ -0,0 +1,56 @@ +{ + { r4 r8.[ c'16^\markup { \pad-markup #0.2 "+0"}] ~ c'2 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" \break + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" \break + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 ~ } + \bar "|" + { c'1 } + \bar "|" \break + { b4^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↓" }} ~ b2. ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b4 ~ b8[ ais8^\markup { \pad-markup #0.2 "+35"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 5↑" }}] ~ ais8.[ a16^\markup { \pad-markup #0.2 "-20"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 7↓" }}] ~ a4 ~ } + \bar "|" + { a1 ~ } + \bar "|" \break + { a1 ~ } + \bar "|" + { a1 ~ } + \bar "|" + { a16[ a8.^\markup { \pad-markup #0.2 "+16"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 7↑" }}] ~ a2. } + \bar "|" + { gis4^\markup { \pad-markup #0.2 "-13"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↑" }} ~ gis2. ~ } + \bar "|" \break + { gis8.[ gis16^\markup { \pad-markup #0.2 "-40"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↓" }}] ~ gis2. ~ } + \bar "|" + { gis4 ~ gis8[ f8^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 1↑" }}] ~ f4 fis4^\markup { \pad-markup #0.2 "-5"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↓" }} ~ } + \bar "|" + { fis2. ~ fis8[ gis8^\markup { \pad-markup #0.2 "-13"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↑" }}] ~ } + \bar "|" + { gis1 ~ } + \bar "|" \break + { gis1 ~ } + \bar "|" + { gis1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/535cc132/535cc132_code.scd b/resources/535cc132/535cc132_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/535cc132/535cc132_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/535cc132/535cc132_mus_model.json b/resources/535cc132/535cc132_mus_model.json new file mode 100644 index 0000000..dcd0704 --- /dev/null +++ b/resources/535cc132/535cc132_mus_model.json @@ -0,0 +1,91 @@ +{ +"music_data": +[ + [ + [ + [ [ [ 1, -1, 0, 0, -1, 1 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 1 ], [ "Rest" ] ], 0 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ] ], 8.375 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 2 ], [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ] ], 1.5 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ] ], 0.875 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -2, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, -1, 1, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ] ], 10.75 ] + ], + [ + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ], [ 1, -1, 0, 0, -1, 1 ], [ 2, -1, 0, -1, -1, 1 ] ], 0.625 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ], [ 0, -1, 0, 1, -1, 1 ], [ 2, -1, 0, -1, -1, 1 ] ], 0.625 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 1, -1, 1 ], [ 2, -1, 0, -1, -1, 1 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 1, -1, 1 ], [ 2, -1, 0, -1, -1, 1 ] ], 0.875 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 1, -1, 1 ], [ 2, -1, 0, 0, -1, 0 ] ], 1.25 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 1 ], [ 2, -1, 0, 0, -1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 0 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 2 ], [ 1, -1, 0, 0, 0, 1 ] ], 0 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, -1, 1, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 2 ], [ 1, -1, 0, 0, 0, 1 ] ], 1.875 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, -1, 1, 0, -1, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 8 ] + ], + [ + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, -1, -1, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 1 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, 0, -1, 0, -1, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 1 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 1.625 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, -1, 0, 0, 0, 2 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 0.875 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 1.25 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, 0, 1 ] ], 8.625 ] + ], + [ + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 2, -1, -1, 0, -1, 1 ] ], 1.75 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 2, 0, 0, -1, -1, 1 ] ], 1.875 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, 0, -1, 0, 0, 1 ] ], 0.625 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, 0, 0, 0, -1, 1 ] ], 0.625 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 9.25 ] + ], + [ + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, -1, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 1.875 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 1, -1, -1, 0, -1, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 1.625 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 1, -1, 0, 0, -2, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 1.625 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 1, -2, 0, 0, -1, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 1 ], + [ [ [ 1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 1 ] ], 9.75 ] + ], + [ + [ [ [ 1, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 1 ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 1 ] ], 1.25 ], + [ [ [ "Rest" ], [ "Rest" ], [ -2, 0, 0, 0, 1, 2 ], [ 0, 0, 0, 0, 1, 1 ] ], 1.875 ], + [ [ [ "Rest" ], [ "Rest" ], [ -2, 1, 0, 0, 1, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 1.25 ], + [ [ [ "Rest" ], [ "Rest" ], [ -1, 0, -1, 0, 1, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 2 ], + [ [ [ "Rest" ], [ "Rest" ], [ -2, 0, 0, 1, 1, 1 ], [ 0, 0, 0, 0, 1, 1 ] ], 7.625 ], + [ [ [ "Rest" ], [ "Rest" ], [ -2, 0, 0, 1, 1, 1 ], [ "Rest" ] ], 1 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 8.375 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "535cc132", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/64b535ef/64b535ef_code.scd b/resources/64b535ef/64b535ef_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/64b535ef/64b535ef_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/64b535ef/64b535ef_mus_model.json b/resources/64b535ef/64b535ef_mus_model.json new file mode 100644 index 0000000..f9a31aa --- /dev/null +++ b/resources/64b535ef/64b535ef_mus_model.json @@ -0,0 +1,97 @@ +{ +"music_data": +[ + [ + [ + [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.625 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ] ], 1.5 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, -1, 0, 0, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0 ] ], 0.75 ] + ], + [ + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ "Rest" ] ], 1.5 ], + [ [ [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ "Rest" ] ], 1.25 ], + [ [ [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ] ], 1.375 ], + [ [ [ -1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ] ], 0.75 ], + [ [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0 ], [ "Rest" ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1 ], [ "Rest" ] ], 1.125 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, -1, 0, 0, 0 ], [ "Rest" ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ] ], 1 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ] ], 0.875 ] + ], + [ + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, -1, 0, -1, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, -1, 0, -1, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 1, 0, -1, 0, -1, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 0, 0, 0, 0, -1, 1 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 1, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ] ], 1.375 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ] ], 1.375 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ], [ 2, 0, 0, -1, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 0, 0, 1, 0, -1, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1 ] + ], + [ + [ [ [ "Rest" ], [ 1, 0, 0, -1, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ "Rest" ], [ 0, -1, 0, 1, -1, 0 ], [ 2, 0, 0, 0, -1, -1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 0, -1, 0, 1, -1, 0 ], [ 2, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ], [ 2, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.125 ], + [ [ [ "Rest" ], [ 1, -1, -1, 0, -1, 0 ], [ 1, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1 ], + [ [ [ "Rest" ], [ 1, -1, -1, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ "Rest" ], [ 1, -1, -1, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ "Rest" ], [ 1, -1, 0, 0, -2, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.375 ], + [ [ [ "Rest" ], [ 1, -1, 0, 0, -2, 0 ], [ 2, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ], [ 2, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 3.375 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ], [ 2, -2, 0, 0, -1, 0 ], [ "Rest" ] ], 1.5 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ "Rest" ] ], 1.375 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 1, 0, 0, 0, 0, -2 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 1, 0, -1 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ] +], +"cur_uid": "64b535ef", +"ref_uid": "nil", +"order_seed": 320463, +"dur_seed": 903977, +"motifs_seed": 895384, +"entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], +"passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 1 ], [ 0, 3, 2, 3, 3, 2 ], [ ] ], + [ [ 2, 1 ], [ 3, 0, 3 ], [ ] ], + [ [ 1 ], [ 3, 2, 0 ], [ ] ] +], +"sus_weights": [ 0.75, 0.69, 0.75 ], +"order_size": [ 2, 6 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/652c77ba/652c77ba_code.scd b/resources/652c77ba/652c77ba_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/652c77ba/652c77ba_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/652c77ba/652c77ba_mus_model.json b/resources/652c77ba/652c77ba_mus_model.json new file mode 100644 index 0000000..a671438 --- /dev/null +++ b/resources/652c77ba/652c77ba_mus_model.json @@ -0,0 +1,97 @@ +{ +"music_data": +[ + [ + [ + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.75 ] + ], + [ + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 1, 0, 0, 0, 0, -1 ], [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.5 ], + [ [ [ 1, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ 0, 1, 0, 0, 0, 0 ], [ -1, 0, 0, 1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.375 ], + [ [ [ 0, 0, 0, 1, 0, 0 ], [ -1, 0, 0, 1, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 1, 0, 0 ], [ 0, 0, -1, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.625 ], + [ [ [ 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.125 ], + [ [ [ 1, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ] + ], + [ + [ [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, -1, 0, -1, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, -1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, 0, -1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 1, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ 1, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ] ], 0.625 ], + [ [ [ 1, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1.375 ], + [ [ [ 1, 0, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1.375 ], + [ [ [ 2, 0, 0, -1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1.5 ], + [ [ [ 2, 0, 0, 0, -1, -1 ], [ 0, 0, 0, 0, -1, 0 ], [ 0, 0, 1, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 0.75 ], + [ [ [ 2, 0, 0, 0, -1, -1 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 1 ] + ], + [ + [ [ [ 2, 0, 0, 0, -1, -1 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, 0, 0, -1, -1, 0 ] ], 0.75 ], + [ [ [ 2, 0, 0, 0, -1, -1 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 1, -1, 0 ] ], 1 ], + [ [ [ 2, -1, 0, 0, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 1, -1, 0 ] ], 0.875 ], + [ [ [ 2, -1, 0, 0, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 1 ] ], 1.25 ], + [ [ [ 1, -1, 0, 1, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 1 ] ], 1.125 ], + [ [ [ 1, -1, 0, 1, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 0 ] ], 1 ], + [ [ [ 1, 0, 0, 0, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, -1, 0, -1, 0 ] ], 0.625 ], + [ [ [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, 0, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -2, 0 ] ], 1.375 ], + [ [ [ 2, -2, 0, 0, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -2, 0 ] ], 0.875 ], + [ [ [ 2, -2, 0, 0, -1, 0 ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 1 ] ], 3.375 ], + [ [ [ 2, -2, 0, 0, -1, 0 ], [ "Rest" ], [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ] ], 1.5 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ] ], 1.375 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 1, 0, 0, 0, 0, -2 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 1, 0, -1 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 0, 0, 0, 1, 0, -1 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ], + [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ] ] +], +"cur_uid": "652c77ba", +"ref_uid": "nil", +"order_seed": 320463, +"dur_seed": 903977, +"motifs_seed": 895384, +"entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], +"passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], +"ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 1 ], [ 0, 3, 2, 3, 3, 2 ], [ ] ], + [ [ 2, 1 ], [ 3, 0, 3 ], [ ] ], + [ [ 1 ], [ 3, 2, 0 ], [ ] ] +], +"sus_weights": [ 0.75, 0.69, 0.75 ], +"order_size": [ 2, 6 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/66f6a618/66f6a618_code.scd b/resources/66f6a618/66f6a618_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/66f6a618/66f6a618_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/66f6a618/66f6a618_mus_model.json b/resources/66f6a618/66f6a618_mus_model.json new file mode 100644 index 0000000..9b60dbd --- /dev/null +++ b/resources/66f6a618/66f6a618_mus_model.json @@ -0,0 +1,91 @@ +{ +"music_data": +[ + [ + [ + [ [ [ "Rest" ], [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 8.375 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.875 ], + [ [ [ 1, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ 0, -1, 1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, -1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ] ], 10.75 ] + ], + [ + [ [ [ 1, -1, 0, -1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, -1, 0, -1, -1, 0 ] ], 0.625 ], + [ [ [ 1, -1, 0, -1, -1, 0 ], [ 0, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, -1, 0, -1, -1, 0 ] ], 0.625 ], + [ [ [ 0, -1, 0, 0, -1, 0 ], [ 0, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, -1, 0, -1, -1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, 0, -1, -1 ], [ 0, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, -1, 0, -1, -1, 0 ] ], 0.875 ], + [ [ [ 1, -1, 0, 0, -1, -1 ], [ 0, -1, 0, 1, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, -1, 0, 0, -1, -1 ] ], 1.25 ], + [ [ [ 1, -1, 0, 0, -1, -1 ], [ 1, -1, -1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, -1, 0, 0, -1, -1 ] ], 1.75 ], + [ [ [ 1, -1, 0, 0, -1, -1 ], [ 1, -1, -1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 0 ], + [ [ [ 1, -1, 0, 0, -1, -1 ], [ 0, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 0 ], + [ [ [ 0, -1, 1, 0, -1, 0 ], [ 0, -1, 0, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 1.875 ], + [ [ [ 0, -1, 1, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 8 ] + ], + [ + [ [ [ 0, -1, -1, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 0, 0, -1, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 1, -1, 0, -1, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 1.625 ], + [ [ [ -1, -1, 0, 0, 0, 1 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 0.875 ], + [ [ [ 0, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 1.25 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, -1, 0, 0, 0, 0 ] ], 8.625 ] + ], + [ + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, -1, -1, 0, -1, 0 ] ], 1.75 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 2, 0, 0, -1, -1, 0 ] ], 1.875 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, 0, -1, 0, 0, 0 ] ], 0.625 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 1, 0, 0, 0, -1, 0 ] ], 0.625 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 9.25 ] + ], + [ + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.875 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 1, -1, -1, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.625 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, -2, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.625 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 1, -2, 0, 0, -1, 0 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 1 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, -1 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 9.75 ] + ], + [ + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, -1 ], [ 1, -1, 0, 0, -1, 0 ], [ 0, 0, 0, 0, 1, 0 ] ], 0 ], + [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, -1 ], [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ -2, 0, 0, 0, 1, 1 ], [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ] ], 1.875 ], + [ [ [ "Rest" ], [ -2, 1, 0, 0, 1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ -1, 0, -1, 0, 1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ] ], 2 ], + [ [ [ "Rest" ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 1, 0 ] ], 7.625 ], + [ [ [ "Rest" ], [ -2, 0, 0, 1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 1 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 8.375 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "66f6a618", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/66f6a618/lilypond/part_I.ly b/resources/66f6a618/lilypond/part_I.ly new file mode 100644 index 0000000..9965ef5 --- /dev/null +++ b/resources/66f6a618/lilypond/part_I.ly @@ -0,0 +1,110 @@ +{ + { b4^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }} ~ b2. ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b2 d'4^\markup { \pad-markup #0.2 "-22"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }} ~ d'4 ~ } + \bar "|" \break + { d'2. ~ d'8.[ dis'16^\markup { \pad-markup #0.2 "+6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }}] ~ } + \bar "|" + { dis'1 ~ } + \bar "|" + { dis'4 ~ dis'8.[ f'16^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 11↑" }}] ~ f'2 ~ } + \bar "|" + { f'1 ~ } + \bar "|" \break + { f'1 ~ } + \bar "|" + { f'1 ~ } + \bar "|" + { f'1 ~ } + \bar "|" + { f'1 ~ } + \bar "|" \break + { f'1 ~ } + \bar "|" + { f'1 ~ } + \bar "|" + { f'1 ~ } + \bar "|" + { f'1 ~ } + \bar "|" \break + { f'1 ~ } + \bar "|" + { f'1 ~ } + \bar "|" + { f'2 ~ f'16[ gis'8.^\markup { \pad-markup #0.2 "-40"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↓" }}] ~ gis'4 ~ } + \bar "|" + { gis'4 ~ gis'8.[ a'16^\markup { \pad-markup #0.2 "-20"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 7↓" }}] ~ a'2 ~ } + \bar "|" \break + { a'4 ~ a'8[ gis'8^\markup { \pad-markup #0.2 "+14"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↓" }}] ~ gis'8.[ fis'16^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ fis'4 } + \bar "|" + { fis'4^\markup { \pad-markup #0.2 "-49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↑" }} ~ fis'2. ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'2 ~ fis'16[ r8.] r4 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/66f6a618/lilypond/part_II.ly b/resources/66f6a618/lilypond/part_II.ly new file mode 100644 index 0000000..bcf9cca --- /dev/null +++ b/resources/66f6a618/lilypond/part_II.ly @@ -0,0 +1,110 @@ +{ + { b4^\markup { \pad-markup #0.2 "+47"} ~ b2. ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b2 ~ b16[ r8.] r4 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/66f6a618/lilypond/part_III.ly b/resources/66f6a618/lilypond/part_III.ly new file mode 100644 index 0000000..f19e391 --- /dev/null +++ b/resources/66f6a618/lilypond/part_III.ly @@ -0,0 +1,110 @@ +{ + { b4^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 1↑" }} ~ b2. ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" \break + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b1 ~ } + \bar "|" + { b2. ~ b16[ a8.^\markup { \pad-markup #0.2 "+16"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↑" }}] ~ } + \bar "|" \break + { a1 ~ } + \bar "|" + { a2 ~ a16[ gis8.^\markup { \pad-markup #0.2 "-40"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↓" }}] ~ gis4 ~ } + \bar "|" + { gis4 ~ gis8.[ gis16^\markup { \pad-markup #0.2 "-13"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↑" }}] ~ gis2 ~ } + \bar "|" + { gis4 ~ gis8[ fis8^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↑" }}] ~ fis2 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" + { fis1 ~ } + \bar "|" \break + { fis1 ~ } + \bar "|" + { fis2 ~ fis8[ f8^\markup { \pad-markup #0.2 "-2"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ f4 ~ } + \bar "|" + { f2 ~ f16[ gis8.^\markup { \pad-markup #0.2 "-40"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↓" }}] ~ gis4 ~ } + \bar "|" + { gis4 ~ gis8[ fis8^\markup { \pad-markup #0.2 "-5"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 11↓" }}] ~ fis2 ~ } + \bar "|" \break + { fis8.[ e16^\markup { \pad-markup #0.2 "+45"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↓" }}] ~ e4 ~ e8.[ e16^\markup { \pad-markup #0.2 "-41"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↓" }}] ~ e4 ~ } + \bar "|" + { e1 ~ } + \bar "|" + { e1 ~ } + \bar "|" + { e1 ~ } + \bar "|" \break + { e1 ~ } + \bar "|" + { e1 ~ } + \bar "|" + { e8.[ d16^\markup { \pad-markup #0.2 "-8"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↑" }}] ~ d2. ~ } + \bar "|" + { d8[ cis8^\markup { \pad-markup #0.2 "-47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↑" }}] ~ cis2 d4^\markup { \pad-markup #0.2 "-35"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↓" }} ~ } + \bar "|" \break + { d2. dis4^\markup { \pad-markup #0.2 "+20"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 7↑" }} ~ } + \bar "|" + { dis1 ~ } + \bar "|" + { dis1 ~ } + \bar "|" + { dis1 ~ } + \bar "|" \break + { dis1 ~ } + \bar "|" + { dis16[ r8.] r2. } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/66f6a618/lilypond/part_IV.ly b/resources/66f6a618/lilypond/part_IV.ly new file mode 100644 index 0000000..7b99ccd --- /dev/null +++ b/resources/66f6a618/lilypond/part_IV.ly @@ -0,0 +1,110 @@ +{ + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r8.[ gis16^\markup { \pad-markup #0.2 "-13"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 13↑" }}] ~ gis2 ~ gis8.[ fis16^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↑" }}] ~ } + \bar "|" + { fis4 ~ fis8[ e8^\markup { \pad-markup #0.2 "+45"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 3↓" }}] ~ e4 dis4^\markup { \pad-markup #0.2 "+33"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 5↑" }} ~ } + \bar "|" + { dis8[ d8^\markup { \pad-markup #0.2 "-22"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 7↓" }}] ~ d2. ~ } + \bar "|" + { d1 ~ } + \bar "|" \break + { d1 ~ } + \bar "|" + { d1 ~ } + \bar "|" + { d1 ~ } + \bar "|" + { d1 ~ } + \bar "|" \break + { d8[ b,8^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 1↑" }}] ~ b,4 dis2^\markup { \pad-markup #0.2 "+6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 13↓" }} ~ } + \bar "|" + { dis1 ~ } + \bar "|" + { dis4 ~ dis8.[ dis16^\markup { \pad-markup #0.2 "+33"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 5↑" }}] ~ dis2 ~ } + \bar "|" + { dis1 ~ } + \bar "|" \break + { dis1 ~ } + \bar "|" + { dis1 ~ } + \bar "|" + { dis1 ~ } + \bar "|" + { dis4 ~ dis8[ cis8^\markup { \pad-markup #0.2 "+12"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↓" }}] ~ cis4 ~ cis8[ dis8^\markup { \pad-markup #0.2 "-38"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 5↓" }}] ~ } + \bar "|" \break + { dis4 ~ dis8[ d8^\markup { \pad-markup #0.2 "-22"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↓" }}] ~ d2 ~ } + \bar "|" + { d8.[ cis16^\markup { \pad-markup #0.2 "+39"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↑" }}] ~ cis4 ~ cis8[ b,8^\markup { \pad-markup #0.2 "+47"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 1↑" }}] ~ b,4 ~ } + \bar "|" + { b,4 c4^\markup { \pad-markup #0.2 "+0"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 11↑" }} ~ c2 ~ } + \bar "|" + { c1 ~ } + \bar "|" \break + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" \break + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" \break + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" \break + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" + { c1 ~ } + \bar "|" \break + { c1 ~ } + \bar "|" + { c2 ~ c16[ r8.] r4 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/6fb60ab6/6fb60ab6_code.scd b/resources/6fb60ab6/6fb60ab6_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/6fb60ab6/6fb60ab6_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/6fb60ab6/6fb60ab6_mus_model.json b/resources/6fb60ab6/6fb60ab6_mus_model.json new file mode 100644 index 0000000..1fc0586 --- /dev/null +++ b/resources/6fb60ab6/6fb60ab6_mus_model.json @@ -0,0 +1,60 @@ +{ +"music_data": +[ + [ + [ + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0.625 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ], [ 1, 0, 0, -1, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 2, -1, 0, -1, 0, 0 ] ], 1.875 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, -1, 0, -1, 2, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, -1, 1, -1, 1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.25 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 0, -1, 0, 0, 1, 0 ] ], 1.625 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.875 ] + ], + [ + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 2, -1, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, -1, 0, -1, 2, 0 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.125 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, 0, -1, -1, 1, 0 ], [ 1, 0, 0, -1, 1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, -2, 0, -1, 1, 1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, 0, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ "Rest" ], [ 1, 0, 0, -1, 1, 0 ] ], 1.125 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1.25 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "6fb60ab6", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "false", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/761e4585/761e4585_code.scd b/resources/761e4585/761e4585_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/761e4585/761e4585_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/761e4585/761e4585_mus_model.json b/resources/761e4585/761e4585_mus_model.json new file mode 100644 index 0000000..03ecbc9 --- /dev/null +++ b/resources/761e4585/761e4585_mus_model.json @@ -0,0 +1,81 @@ +{ +"music_data": +[ + [ + [ + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ 1, -1, 0, -1, 1, 0 ] ], 1.75 ], + [ [ [ "Rest" ], [ "Rest" ], [ 1, -1, 1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.625 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.875 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ 2, -1, 0, -1, 1, -1 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.375 ], + [ [ [ "Rest" ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 2, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0 ], + [ [ [ "Rest" ], [ 0, 0, 0, -1, 1, 0 ], [ 1, -1, 0, -1, 2, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0.5 ], + [ [ [ "Rest" ], [ 0, -1, 0, -1, 1, 1 ], [ 1, -1, 0, -1, 2, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0 ], + [ [ [ "Rest" ], [ 0, -1, 0, -1, 1, 1 ], [ 1, 0, 0, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.25 ], + [ [ [ "Rest" ], [ 0, -1, 0, -1, 1, 1 ], [ 2, -1, -1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0 ], + [ [ [ "Rest" ], [ 1, -1, -1, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.875 ] + ], + [ + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 1, -1, -1, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 1.625 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 1, -1, 0, -1, 2, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, 0, 1, 0 ], [ 2, -1, 0, -1, 0, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, -1, 1, 1 ], [ 2, -1, 0, -1, 0, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 0, -1, 0, -1, 1, 1 ], [ 1, -1, 0, -1, 1, 1 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 1, -1, -1, -1, 1, 0 ], [ 1, -1, 0, -1, 1, 1 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.875 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 1, -1, -1, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.375 ] + ], + [ + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 1, -1, -1, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ "Rest" ] ], 1.375 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 0, 0, 0, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ "Rest" ] ], 1 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -1, -1, -2, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ "Rest" ] ], 1.125 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 1, -1, -1, -1, 2, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ "Rest" ] ], 1.625 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, -1, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ "Rest" ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 1, -1, 1, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ "Rest" ] ], 1.125 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ "Rest" ] ], 1.25 ] + ], + [ + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 2, -1, -1, -1, 1, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, -1, 0, 0, 1, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.25 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 2, -1, 0, -1, 1, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.25 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 1 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 3, -1, 0, -2, 1, 0 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.25 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -2, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ 2, -2, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "761e4585", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/761e4585/lilypond/part_I.ly b/resources/761e4585/lilypond/part_I.ly new file mode 100644 index 0000000..c9e3103 --- /dev/null +++ b/resources/761e4585/lilypond/part_I.ly @@ -0,0 +1,36 @@ +{ + { cis'4^\markup { \pad-markup #0.2 "-19"} ~ cis'2. ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'2. ~ cis'8.[ dis'16^\markup { \pad-markup #0.2 "+12"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↓" }}] ~ } + \bar "|" + { dis'1 ~ } + \bar "|" + { dis'1 ~ } + \bar "|" + { dis'1 ~ } + \bar "|" \break + { dis'1 ~ } + \bar "|" + { dis'8.[ r16] r2. } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r2. r16[ dis'8.^\markup { \pad-markup #0.2 "+12"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↓" }}] ~ } + \bar "|" + { dis'1 ~ } + \bar "|" + { dis'1 ~ } + \bar "|" \break + { dis'4 ~ dis'8.[ r16] r2 } +\bar "||" +} \ No newline at end of file diff --git a/resources/761e4585/lilypond/part_II.ly b/resources/761e4585/lilypond/part_II.ly new file mode 100644 index 0000000..50dda71 --- /dev/null +++ b/resources/761e4585/lilypond/part_II.ly @@ -0,0 +1,36 @@ +{ + { r2. r8[ f'8^\markup { \pad-markup #0.2 "-33"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↑" }}] ~ } + \bar "|" + { f'8.[ fis'16^\markup { \pad-markup #0.2 "-21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↓" }}] ~ fis'4 ~ fis'8[ e'8^\markup { \pad-markup #0.2 "+40"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↓" }}] ~ e'4 ~ } + \bar "|" + { e'4 ~ e'16[ fis'8.^\markup { \pad-markup #0.2 "+32"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↑" }}] ~ fis'16[ gis'8.^\markup { \pad-markup #0.2 "-18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↑" }}] ~ gis'4 ~ } + \bar "|" + { gis'8.[ a'16^\markup { \pad-markup #0.2 "-6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↓" }}] ~ a'2. ~ } + \bar "|" \break + { a'2. ~ a'8.[ fis'16^\markup { \pad-markup #0.2 "+32"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↑" }}] ~ } + \bar "|" + { fis'2. ~ fis'16[ g'8.^\markup { \pad-markup #0.2 "+29"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↓" }}] ~ } + \bar "|" + { g'2 ~ g'8.[ a'16^\markup { \pad-markup #0.2 "+21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }}] ~ a'4 ~ } + \bar "|" + { a'1 ~ } + \bar "|" \break + { a'2 a'4^\markup { \pad-markup #0.2 "-6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 1↑" }} ~ a'4 ~ } + \bar "|" + { a'1 ~ } + \bar "|" + { a'1 ~ } + \bar "|" + { a'1 ~ } + \bar "|" \break + { a'1 ~ } + \bar "|" + { a'2. ~ a'16[ ais'8.^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↑" }}] ~ } + \bar "|" + { ais'4 ~ ais'8.[ cis''16^\markup { \pad-markup #0.2 "-19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 1↑" }}] ~ cis''2 ~ } + \bar "|" + { cis''16[ d''8.^\markup { \pad-markup #0.2 "+19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 13↑" }}] ~ d''2 ~ d''16[ dis''8.^\markup { \pad-markup #0.2 "+12"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 1↑" }}] ~ } + \bar "|" \break + { dis''4 ~ dis''8.[ r16] r2 } +\bar "||" +} \ No newline at end of file diff --git a/resources/761e4585/lilypond/part_III.ly b/resources/761e4585/lilypond/part_III.ly new file mode 100644 index 0000000..fd0f304 --- /dev/null +++ b/resources/761e4585/lilypond/part_III.ly @@ -0,0 +1,36 @@ +{ + { r2. r8[ ais8^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 7↑" }}] ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais4 ~ ais16[ gis8.^\markup { \pad-markup #0.2 "-18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 3↑" }}] ~ gis16[ a8.^\markup { \pad-markup #0.2 "+21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↑" }}] ~ a4 ~ } + \bar "|" + { a8.[ a16^\markup { \pad-markup #0.2 "-6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 1↑" }}] ~ a2. ~ } + \bar "|" \break + { a2. ~ a8.[ ais16^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↑" }}] ~ } + \bar "|" + { ais1 ~ } + \bar "|" + { ais2 ~ ais8.[ a16^\markup { \pad-markup #0.2 "+21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↑" }}] ~ a4 ~ } + \bar "|" + { a2 ~ a16[ a8.^\markup { \pad-markup #0.2 "-6"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↓" }}] ~ a4 ~ } + \bar "|" \break + { a1 ~ } + \bar "|" + { a2. ~ a8[ gis8^\markup { \pad-markup #0.2 "-18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ } + \bar "|" + { gis4 ~ gis8[ b8^\markup { \pad-markup #0.2 "+25"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 7↓" }}] ~ b4 ~ b8.[ d'16^\markup { \pad-markup #0.2 "+46"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 11↑" }}] ~ } + \bar "|" + { d'2. d'4^\markup { \pad-markup #0.2 "-8"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "II"\normal-size-super " 3↓" }} ~ } + \bar "|" \break + { d'2 ~ d'8[ f'8^\markup { \pad-markup #0.2 "-33"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }}] ~ f'4 ~ } + \bar "|" + { f'8.[ fis'16^\markup { \pad-markup #0.2 "-21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ fis'2. ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'4 ~ fis'8.[ r16] r2 } +\bar "||" +} \ No newline at end of file diff --git a/resources/761e4585/lilypond/part_IV.ly b/resources/761e4585/lilypond/part_IV.ly new file mode 100644 index 0000000..075972b --- /dev/null +++ b/resources/761e4585/lilypond/part_IV.ly @@ -0,0 +1,36 @@ +{ + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r8[ cis'8^\markup { \pad-markup #0.2 "-19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 1↑" }}] ~ cis'2. ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'4 ~ cis'8.[ r16] r2 } +\bar "||" +} \ No newline at end of file diff --git a/resources/7cc3121d/7cc3121d_code.scd b/resources/7cc3121d/7cc3121d_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/7cc3121d/7cc3121d_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/7cc3121d/7cc3121d_mus_model.json b/resources/7cc3121d/7cc3121d_mus_model.json new file mode 100644 index 0000000..53c2570 --- /dev/null +++ b/resources/7cc3121d/7cc3121d_mus_model.json @@ -0,0 +1,91 @@ +{ +"music_data": +[ + [ + [ + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ] ], 0 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ] ], 8.375 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, -1, 0, 0, -1, 2 ], [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ] ], 1.5 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ] ], 0.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -2, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ] ], 0.75 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, -1, 1, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ] ], 0.75 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, -1, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ] ], 10.75 ] + ], + [ + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, -1, -1, 1 ], [ 0, -1, 0, 0, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ] ], 0.625 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, -1, -1, 1 ], [ -1, -1, 0, 1, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ] ], 0.625 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, -1, 0, 0, -1, 1 ], [ -1, -1, 0, 1, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ] ], 0.75 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 0 ], [ -1, -1, 0, 1, -1, 1 ], [ 1, -1, 0, -1, -1, 1 ] ], 0.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 0 ], [ -1, -1, 0, 1, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.25 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 0 ], [ 0, -1, -1, 0, -1, 1 ], [ 1, -1, 0, 0, -1, 0 ] ], 1.75 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 0 ], [ 0, -1, -1, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 0 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, 0, -1, 0 ], [ -1, -1, 0, 0, -1, 2 ], [ 0, -1, 0, 0, 0, 1 ] ], 0 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, -1, 1, 0, -1, 1 ], [ -1, -1, 0, 0, -1, 2 ], [ 0, -1, 0, 0, 0, 1 ] ], 1.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, -1, 1, 0, -1, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 8 ] + ], + [ + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, -1, -1, 0, 0, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 1 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, 0, -1, 0, -1, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 1 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ 0, -1, 0, -1, -1, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 1.625 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, -1, 0, 0, 0, 2 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 0.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -1, -1, 0, 0, -1, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 1.25 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, -1, 0, 0, 0, 1 ] ], 8.625 ] + ], + [ + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 1, -1, -1, 0, -1, 1 ] ], 1.75 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 1, 0, 0, -1, -1, 1 ] ], 1.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, 0, -1, 0, 0, 1 ] ], 0.625 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ 0, 0, 0, 0, -1, 1 ] ], 0.625 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, 0, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 9.25 ] + ], + [ + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, -1, 0, 0, 0, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 1.875 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ 0, -1, -1, 0, -1, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 1.625 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ 0, -1, 0, 0, -2, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 1.625 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ 0, -2, 0, 0, -1, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 1 ], + [ [ [ 0, -1, 0, 0, -1, 1 ], [ -2, 0, 0, 0, 0, 1 ], [ -1, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 0, 1, 1 ] ], 9.75 ] + ], + [ + [ [ [ 0, -1, 0, 0, -1, 1 ], [ "Rest" ], [ -1, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 0, 1, 1 ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ -1, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 0, 1, 1 ] ], 1.25 ], + [ [ [ "Rest" ], [ "Rest" ], [ -3, 0, 0, 0, 1, 2 ], [ -1, 0, 0, 0, 1, 1 ] ], 1.875 ], + [ [ [ "Rest" ], [ "Rest" ], [ -3, 1, 0, 0, 1, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 1.25 ], + [ [ [ "Rest" ], [ "Rest" ], [ -2, 0, -1, 0, 1, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 2 ], + [ [ [ "Rest" ], [ "Rest" ], [ -3, 0, 0, 1, 1, 1 ], [ -1, 0, 0, 0, 1, 1 ] ], 7.625 ], + [ [ [ "Rest" ], [ "Rest" ], [ -3, 0, 0, 1, 1, 1 ], [ "Rest" ] ], 1 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 8.375 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "7cc3121d", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/7ebbb471/7ebbb471_code.scd b/resources/7ebbb471/7ebbb471_code.scd new file mode 100644 index 0000000..3eb3cbd --- /dev/null +++ b/resources/7ebbb471/7ebbb471_code.scd @@ -0,0 +1,1058 @@ +( +// helper funcs +var hsArrayToCents, pDist, hdSum, hsChordalDistance, hsArrayToFreq; + +// score funcs +var isInRange, spacingScore, rangeScore, intervalScore, inclusionScore; + +// subroutines +var genTuples, initVoices, genOrders, genSubMotif, updateVoices, genDurFunc, genStepFunc; + +// primary routines +var genMotif, genSecondarySeq; + +// audition funcs +var genPatterns, genMidiPatterns; + +// resource management funcs +var seedFunc, genUID, writeResources, stringifyToDepth, setSeeds, sanityCheck, +msgInterpret, loadLedgerFile, loadLedgerJSON, loadModelFile, loadModelJSON, +setGlobalVars, globalVarsToDict, saveLedger; + +// model vars +//(model and global vars mostly set by OSC funcs +var seq, lastXChanges, +curUID, refUID, orderSeed, durSeed, motifSeed, +entrancesProbVals, passagesProbVals, exitsProbVals, +ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, +orders, susWeights, orderSize, passagesSize, +motifEdited, orderEdited; + +// model aux vars +var entrancesDurFunc, passagesDurFunc, exitsDurFunc, stepFunc; + +// other global vars +var popSize, exPath, dir, primes, dims, tuples, +group, player, ledgerPath, ledger, currentlyPlayingUID, +nameSpaces; + +// install JSON quark +if(Quarks.isInstalled("JSONlib").not, { + Quarks.install("https://github.com/musikinformatik/JSONlib.git"); + thisProcess.recompile; + //HelpBrowser.openHelpFor("Classes/JSONlib"); +}); + + +//------helper funcs + +hsArrayToCents = { + arg hsArray; + hsArray.collect({arg dist, p; dist * 1200 * log2(primes[p][0]/primes[p][1])}).sum +}; + +pDist = { + arg array1, array2, signed = false; + var pDistance; + pDistance = hsArrayToCents.value(array1) - hsArrayToCents.value(array2); + if(signed, {pDistance}, {abs(pDistance)}) +}; + +hdSum = { + arg hsArrays; + var size, distances, mean; + size = hsArrays.size; + distances = (size - 1).collect({arg i; + ((i + 1)..(size - 1)).collect({arg j; + abs(hsArrays[i] - hsArrays[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsChordalDistance = { + arg hsArrays1, hsArrays2; + var size, distances, mean; + size = hsArrays1.size; + distances = hsArrays1.size.collect({arg i; + hsArrays2.size.collect({arg j; + abs(hsArrays1[i] - hsArrays2[j]).collect({arg dist, p; dist * log2(primes[p].product)}).sum + }); + }).flat; + mean = distances.sum / distances.size; + distances.sum + //mean + ((1 / sqrt((pow(distances - mean, 2)).sum / distances.size)) * mean) +}; + +hsArrayToFreq = { + arg array; + array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product +}; + +//------score funcs + +/* +isInRange = { + arg hsArray, min, max; + var cents; + cents = hsArrayToCents.value(hsArray); + (cents >= min) && (cents <= max) +}; +*/ + +spacingScore = { + arg hsArrays, min; + var centsArray; + centsArray = hsArrays.collect({arg hsArray; hsArrayToCents.value(hsArray)}).sort({arg a, b; a < b}); + centsArray.differentiate.drop(1).collect({arg pDistance; if(pDistance >= min, {1}, {0.01})}).sum; +}; + +rangeScore = { + arg hsArray1, hsArray2, min, max, low, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + if((pDistance >= min) && (pDistance <= max), {1}, {low}); +}; + +intervalScore = { + arg hsArray1, hsArray2, mean, sd, signed = false; + var pDistance; + pDistance = pDist.value(hsArray1, hsArray2, signed); + //pDistance.gaussCurve(1, mean, sd) + stepFunc.value(pDistance); +}; + +inclusionScore = { + arg array, test, min = 0.01; + if(array.collect({arg v; v.hash}).includes(test.hash), {min}, {1}); +}; + + +//------subroutines + +genTuples = { + var tuples; + tuples = dims.collect({[-1, 0, 1]}).allTuples.select({arg tuple; (abs(tuple.drop(1)).sum <= 1) && (tuple[0] == 0)}); + tuples = tuples ++ tuples.collect({arg tuple; [-3, -2, -1, 1, 2, 3].collect({arg octTrans; tuple.deepCopy.put(0, octTrans)})}).flatten; +}; + +initVoices = { + var init, voicesInit; + voicesInit = popSize.collect({dims.collect({0})}); + /* + voicesInit = [dims.collect({0})]; + (popSize - 1).do({ + arg rep, new; + rep = dims.rand; + new = voicesInit.last.deepCopy; + new[rep] = new[rep] + [-1, 1].choose(); + voicesInit = voicesInit.add(new); + }); + */ + voicesInit.deepCopy; +}; + +genDurFunc = {arg chordProb, minPad, maxPad, minDur, maxDur, envData, seed; + var env, pTable, durFunc; + env = Env.pairs([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).asSignal(256).asList.asArray; + pTable = env.asRandomTable; + [chordProb, minPad, maxPad, minDur, maxDur, envData].postln; + durFunc = {arg allowChord, pad = false; + var res; + res = if(allowChord.not, { + pTable.tableRand * (maxDur - minDur) + minDur + }, { + if(1.0.rand < chordProb, {0}, {pTable.tableRand * (maxDur - minDur) + minDur}); + }).round(0.125); + if(pad, {res = res + rrand(minPad.asFloat, maxPad.asFloat).round(0.125)}); + if(res.asInteger == res, {res = res.asInteger}); + res + }; + seedFunc.value(durFunc, seed); +}; + +genStepFunc = {arg minStep, maxStep, envData, seed; + var envDataNorm, env, pTable, stepFunc; + [minStep, maxStep, envData].postln; + envDataNorm = ([[0, 0]] ++ envData.clump(2) ++ [[1, 0]]).flop; + envDataNorm = [envDataNorm[0].normalize(minStep, maxStep), envDataNorm[1]].flop; + env = Env.pairs(envDataNorm); + stepFunc = {arg pDist; + env.at(pDist).clip(0.001, 1); + }; + seedFunc.value(stepFunc, seed); +}; + +genOrders = {arg minMotifLength = 1, maxMotifLength = 5, minProgLength = 0, maxProgLength = 5; + ((maxMotifLength - minMotifLength).rand + minMotifLength).collect({ + var noProgIns, noSusIns, noSilentIns, prog, sus, silent, order; + noSusIns = [1, 2, 3].wchoose(susWeights.normalizeSum); + noProgIns = (popSize - noSusIns).rand + 1; + noSilentIns = popSize - noSusIns - noProgIns; + + # prog, sus, silent = (0..(popSize-1)).scramble.clumps([noProgIns, noSusIns, noSilentIns]); + + prog = (prog.scramble ++ ((maxProgLength - minProgLength).rand + minProgLength).collect({prog.choose}).scramble); + if(silent == nil, {silent = []}); + [sus.scramble, prog, silent.scramble] + }); +}; + +updateVoices = {arg ins, sus; + var voices, candidates, nWeights, nProbs, sel; + + voices = lastXChanges.deepCopy.last; + + candidates = sus.collect({arg v; tuples.collect({arg t; voices[v] + t})}).flatten; + candidates = difference(candidates.asSet, voices.asSet).asList; + nProbs = candidates.collect({arg candidate; + var stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore; + + //stepScore = intervalScore.value(voices[ins], candidate, 30, 400, 0.1); + stepScore = intervalScore.value(voices[ins], candidate, 100, 100); + recentlySoundedScore = inclusionScore.value(lastXChanges.flop[ins], candidate, 0); + isInRangeScore = rangeScore.value(candidate, candidate.collect({0}), ranges[ins][0], ranges[ins][1], 0, true); + regScore = spacingScore.value(voices.deepCopy.put(ins, candidate), 300); + hdScore = pow(hdSum.value(voices.deepCopy.put(ins, candidate)), hdExp); + if(hdInvert == 0, {hdScore = 1/hdScore}); + //maybe what you want here is a vector to another root and then favoring movement towards it. + //distScore = pow(hsChordalDistance.value(voices, voices.put(ins, candidate)), 2); + + [stepScore, recentlySoundedScore, isInRangeScore, regScore, hdScore] + }); + + nWeights = passagesWeights; + + //this handles nWeights of 0; mainly for testing + nProbs = nProbs.flop.select({arg scores, s; nWeights[s] != 0}).flop; + nWeights = nWeights.select({arg weight; weight != 0}); + nProbs = nProbs.flop.collect({arg scores, s; + if(scores.sum == 0, {scores}, {scores.normalizeSum * nWeights[s]}) + }); + nProbs = nProbs.flop.collect({arg scores, s; scores.product}).normalizeSum; + + sel = candidates.wchoose(nProbs); + + voices[ins] = sel; + lastXChanges = lastXChanges.add(voices).keep(-5); +}; + +genSubMotif = {arg order, orderIndex, lastState, repeatLast = false, startFromLast = false, isLastOrder = false; + var sus, prog, silent, flatOrder, res, isInChord, allowChord, pad, lastXChangesHold, voices, adder; + # sus, prog, silent = order; + flatOrder = silent ++ sus ++ prog; + lastXChangesHold = lastXChanges.deepCopy; + voices = lastState.deepCopy; + isInChord = popSize.collect({false}); + allowChord = false; + pad = false; + res = []; + "------generating motif".postln; + //need to figure out here if voices move between motifs + flatOrder.do({arg ins, i; + + if(prog.includes(ins) && repeatLast.not, {updateVoices.value(ins, sus)}); + adder = if(silent.includes(ins), {["Rest"]}, {lastXChanges.last.deepCopy[ins]}); + + if(voices[ins] != adder, { + var dur; + + if((sus ++ silent).includes(ins), { + allowChord = (ins != sus.last); + pad = (ins == sus.last); + }, { + if(i < (flatOrder.size - 1), { + allowChord = (isInChord[flatOrder[i + 1]] || (ins == flatOrder[i + 1])).not; + pad = false; + }, { + allowChord = false; + pad = true + }); + }); + if((orderIndex == 0) && sus.includes(ins), { + dur = entrancesDurFunc.value(allowChord, pad); + }, { + dur = passagesDurFunc.value(allowChord, pad); + }); + if(dur == 0, {isInChord[ins] = true}, {isInChord = popSize.collect({false})}); + + voices[ins] = adder; + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + + // pad ending + if(orderIndex == (orders.size - 1), { + (0..(popSize-1)).scramble.do({arg ins; + if(res.last.first[ins] != ["Rest"], { + var dur; + voices[ins] = ["Rest"]; + allowChord = (voices != popSize.collect({["Rest"]})); + pad = allowChord.not; + dur = exitsDurFunc.value(allowChord, pad); + res = res.add([voices.deepCopy.postln, dur]); + }); + }); + }); + + //format and return + if(startFromLast, {lastXChanges = lastXChangesHold.deepCopy}); + res; +}; + + +//------primary routines + +genMotif = { + var repeats, fSeq, fDur, durAdd; + + repeats = 1; + fSeq = []; + + repeats.do({arg index; + var motif; + + motif = []; + + orders.do({arg order, o; + var lastState, subMotif; + lastState = if(o == 0, {popSize.collect({["Rest"]})}, {motif.last.last.first}); + subMotif = genSubMotif.value(order, o, lastState, isLastOrder: o == (orders.size - 1)); + motif = motif.add(subMotif); + + }); + + sanityCheck.value(motif, index); + + fSeq = fSeq.add(motif); + }); + + //round last duration to measure + fDur = fSeq.flatten.flatten.slice(nil, 1).sum; + durAdd = fDur.round(4) - fDur; + if(durAdd < 0, {durAdd = 4 - durAdd}); + fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] = fSeq[0][orders.size - 1][fSeq[0][orders.size - 1].size - 1][1] + durAdd; + + fSeq +}; + +genSecondarySeq = {arg seq; + var curdles, fSeq; + curdles = []; + while({curdles.sum < seq.size}, {curdles = curdles ++ [3.rand + 1]}); + + fSeq = seq.clumps(curdles).collect({arg clump, m; + var repeats, paddedSeq; + + //add rest + paddedSeq = clump.add([[[popSize.collect({["Rest"]}), 0.5.rand]]]); + + //implement repeats + repeats = [0.rand + 1, 1].wchoose([1, 0].normalizeSum); + repeats.collect({paddedSeq}); + }); + fSeq +}; + + +//------audition funcs + +/* +Event.addEventType(\osc, { + if (~addr.postln.notNil) { + ~addr.sendMsg(~indexPath, ~indexMsg); + ~addr.sendMsg(~seqPath, stringifyToDepth.value(~seqMsg, 3)); + //~addr.sendMsg("/STATE/OPEN", (dir.replace("supercollider", "resources") +/+ ~idMsg +/+ ~idMsg ++ "_gui_state" ++ ".state").standardizePath.postln); + }; +}); +*/ + +Event.addEventType(\osc, { + if (~addr.notNil) { + ~msg; + ~addr.sendMsg(~path, *~msg); + }; +}); + +genPatterns = {arg inSeq, addr, oneShot = false; + var voices, durs, pbinds, res, indices, sectionDurs, msg, ids, seq; + seq = inSeq.collect({arg mSeq; mSeq[0]}); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + pbinds = voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs, attacks, rels, amps; + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + //attacks = 2.collect({rrand(1, 3)}) ++ freqs.drop(2).collect({rrand(0.3, 0.5)}); + attacks = fDurs.collect({arg dur; dur * rrand(0.2, 0.4)}); + //rels = freqs.drop(2).collect({rrand(0.3, 0.5)}) ++ 2.collect({rrand(1, 3)}); + rels = (clumps.size - 1).collect({arg c; + if(clumps[c + 1][0] == ["Rest"], {rrand(1.0, 3.0)}, {rrand(0.3, 0.5)}); + }); + rels = rels.add(rrand(1.0, 3.0)); + amps = freqs.collect({rrand(0.6, 0.99)}); + + [ + Pbind( + \instrument, \string_model, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \attack, Pseq(attacks, 1), + \sustain, Pseq(fDurs, 1), + \release, Pseq(rels, 1), + //\amp, Pseq(amps, 1), + \amp, Pbrown(0.5, 1, 0.5), + \busIndex, v + ), + Pbind( + \instrument, \sine, + \group, group, + \freq, Pseq(freqs, 1), + \dur, Pseq(fDurs, 1), + \sustain, Pseq(fDurs, 1), + \busIndex, v + ) + ] + }).flatten; + if(oneShot.not, { + msg = inSeq.collect({arg mSeq, m; mSeq[1..]}); + //ids = inSeq.collect({arg mSeq, m; mSeq[2]}); + sectionDurs = seq.collect({arg mSeq; mSeq.flatten2(mSeq.maxDepth - 5).flop[1].sum}); + pbinds = pbinds ++ + [ + Pbind( + \type, \osc, + \addr, addr, + \path, "/playing", + \msg, Pseq(msg, 1), + \dur, Pseq(sectionDurs, 1) + ); + ] + }); + res = Ppar(pbinds); + res +}; + +/* +genMidiPatterns = {arg seq; + var voices, durs, patterns, res, mOut, pbRange; + pbRange = 1; //semitones - change this as needed for your situation + mOut = MIDIOut.newByName("TiMidity", "TiMidity port 0").latency_(Server.default.latency); + # voices, durs = seq.flatten2(seq.maxDepth - 5).flop; + res = Ppar( + voices.flop.collect({arg voice, v; + var clumps, hdScores, freqs, fDurs; + + mOut.program(v, 70); + + clumps = voice.separate({arg a, b; a != b }); + freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))}, {Rest(0)})}); + fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum}); + + Pbind( + \type, \midi, + \chan, v, + \noteval, Pseq(freqs.cpsmidi - 24, 1), + \note, Pfunc({ | event | event[\noteval].floor }), + \dur, Pseq(fDurs, 1), + \midiout, mOut, + \amp, 1, + \bend, Pfunc({ + | event | + if (event[\note].isRest.not) { + var pitchbendvalue = event[\noteval].frac.linlin(0, pbRange, 8192, 8192*2).asInteger; + m.bend(v, pitchbendvalue); + }; + 0; // return something other than nil to avoid stopping the pattern + }), + ); + }); + ); + res +}; +*/ + + +//------resource management funcs + +genUID = {Date.seed.asHexString.toLower}; + +seedFunc = {arg func, seed; + var funcArgs, next; + next = Routine({loop{func.valueArray(funcArgs).yield }}); + next.randSeed_(seed); + {arg ...args; funcArgs = args; next.value} +}; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +sanityCheck = {arg motif, index; + //print functions = very helpful + ("----------" + index + "------------").postln; + + motif.flatten.do({arg val, v; + if(v > 0, { + if(motif.flatten[v-1][0].hammingDistance(val[0]) > 1, {"problem 1".postln}); + if(motif.flatten[v-1][0].hammingDistance(val[0]) == 0, {"problem 2".postln}); + }); + val.postln + }); + "***********".postln; +}; + +msgInterpret = {arg in, escapeDoubleQuotes = true, escapeSingleQuotes = true; + var res; + + res = in; + if(res.isNil.not, { + if((res.isArray && res.isString.not), { + res = res.asCompileString; + res = res.replace(" ", "").replace("\n", "").replace("\t", ""); + if(escapeSingleQuotes, {res = res.replace("\'", "")}); + if(escapeDoubleQuotes, {res = res.replace("\"", "")}); + res = res.replace("Rest", "\"Rest\""); + res = res.interpret; + }, { + var tmpRes; + if(res.every({arg char; char.isDecDigit}), {tmpRes = res.asInteger}); + if(res.contains("."), {tmpRes = res.asFloat}); + if(tmpRes != nil, {res = tmpRes}); + }); + }); + res +}; + +writeResources = {arg path, dict; + var file, modelItems, resString; + file = File(path,"w"); + + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + + resString = nameSpaces.collect({arg nameSpace; + var depth = 0, insert = " "; + if(nameSpace == "music_data", {depth = 3; insert = "\n"}); + if(nameSpace == "last_changes", {depth = 1; insert = "\n"}); + if(nameSpace == "order", {depth = 1; insert = "\n"}); + if((nameSpace == "ref_uid") && (dict[nameSpace] == nil), {dict[nameSpace] = "nil"}); + "\"" ++ nameSpace ++ "\":" ++ insert ++ stringifyToDepth.value(dict[nameSpace], depth) + }).join(",\n"); + + resString = "{\n" ++ resString ++ "\n}"; + + file.write(resString); + file.close; + resString +}; + +loadModelFile = {arg path; loadModelJSON.value(File(path, "r").readAllString.parseJSON)}; + +loadModelJSON = {arg jsonObject; + var dict; + dict = Dictionary.with(*nameSpaces.collect({arg nS; nS->msgInterpret.value(jsonObject[nS])})); + dict +}; + +setGlobalVars = {arg dict, skipLastXChanges = false; + var tmpLastXChanges; + tmpLastXChanges = lastXChanges.deepCopy; + // order really matters!!!! + # seq, lastXChanges, curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited = nameSpaces.collect({arg nS; dict[nS]}); + if(skipLastXChanges, {lastXChanges = tmpLastXChanges}); + dict +}; + +globalVarsToDict = { + var modelItems, dict; + // order really matters!!!! + modelItems = [ + seq, lastXChanges, + curUID, refUID, orderSeed, durSeed, motifSeed, + entrancesProbVals, passagesProbVals, exitsProbVals, + ranges, stepProbsVals, passagesWeights, hdExp, hdInvert, + orders, susWeights, orderSize, passagesSize, + motifEdited, orderEdited + ]; + dict = Dictionary.with(*nameSpaces.collect({arg nS, n; nS->modelItems[n]})); +}; + +loadLedgerFile = {arg path; + ledgerPath = path; + loadLedgerJSON.value(File(ledgerPath, "r").readAllString.parseJSON) +}; + +loadLedgerJSON = {arg jsonObject; ledger = jsonObject["ledger"]}; + +saveLedger = {arg ledger, path; + var file; + file = File(path, "w"); + file.write("{\n\"ledger\":\n" ++ stringifyToDepth.value(ledger, 1) ++ "\n}"); + file.close; +}; + +//------global vars + +primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]]; +//ranges = [[-2400, 0], [-1200, 1200], [0, 2400], [0, 2400]]; +exPath = thisProcess.nowExecutingPath; +dir = exPath.dirname; +//popSize = 4; +dims = primes.size; +tuples = genTuples.value(); +//refUID = nil; +group = Group.new; +~group = group; +loadLedgerFile.value(dir +/+ ".." +/+ "resources" +/+ "piece_ledger.json"); +//passagesWeights = [1, 1, 1, 1, 1]; +//susWeights = [1, 1, 1]; +// order really matters!!!! +nameSpaces = [ + "music_data", "last_changes", "cur_uid", "ref_uid", "order_seed", "dur_seed", "motifs_seed", + "entrances_probs_vals","passages_probs_vals", "exits_probs_vals", + "ranges", "step_probs_vals", "passages_weights", "hd_exp", "hd_invert", + "order", "sus_weights", "order_size", "passages_size", + "motif_edited", "order_edited" +]; + + +//------OSC funcs + +OSCdef(\load_ledger, {arg msg, time, addr, port; + loadLedgerFile.value(msg[1].asString); +}, \load_ledger); + +OSCdef(\load_model, {arg msg, time, addr, port; + var dict; + dict = loadModelFile.value(msg[1].asString); + setGlobalVars.value(dict); +}, \load_model); + +OSCdef(\save_ledger, {arg msg, time, addr, port; + msg.postln; + ledger = msgInterpret.value(msg[1].asString.parseJSON["ledger"], false).postln; + //loadLedgerJSON.value(msg[0]) + saveLedger.value(ledger, msg[2].asString); + //loadLedgerFile.value(msg[1].asString); +}, \save_ledger); + +OSCdef(\generate, {arg msg, time, addr, port; + var path, dict, durSeeds, musPath, modelString; + msg.postln; + + path = msg[1].asString; + + dict = loadModelFile.value(path); + setGlobalVars.value(dict, true); + + popSize = ranges.size; + + //refUID.postln; + + loadLedgerFile.value(ledgerPath); + if(ledger == nil, {ledger = ["tmp"]}); + if(ledger.last != "tmp", {ledger = ledger.add("tmp")}); + + if(refUID == nil, {lastXChanges = [initVoices.value().deepCopy]}); + if((refUID != nil) && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + lastXChanges = msgInterpret.value(file.readAllString.parseJSON["last_changes"]); + }); + + refUID.postln; + lastXChanges.collect({arg item; item.postln}); + + durSeeds = seedFunc.value({3.collect({rrand(100000, 999999)})}, durSeed).value.postln; + entrancesDurFunc = genDurFunc.valueArray(entrancesProbVals[..4] ++ [entrancesProbVals[5..]] ++ [durSeeds[0]]); + passagesDurFunc = genDurFunc.valueArray(passagesProbVals[..4] ++ [passagesProbVals[5..]] ++ [durSeeds[1]]); + exitsDurFunc = genDurFunc.valueArray(exitsProbVals[..4] ++ [exitsProbVals[5..]] ++ [durSeeds[2]]); + + if(orders == nil, { + orders = seedFunc.value(genOrders, orderSeed).valueArray(orderSize ++ passagesSize); + //addr.sendMsg("/order", stringifyToDepth.value(orders, 1)); + }); + + stepFunc = genStepFunc.valueArray(stepProbsVals[..1] ++ [stepProbsVals[2..]] ++ [motifSeed]); + seq = seedFunc.value(genMotif, motifSeed).value; + + lastXChanges.collect({arg item; item.postln}); + + dict = globalVarsToDict.value; + modelString = writeResources.value(path, dict); + + //addr.sendMsg("/generated", musPath, stringifyToDepth.value(seq, 3)); + //~seq = seq; + + addr.sendMsg("/generated", path, modelString, ledgerPath); +}, \generate); + + +OSCdef(\commit, {arg msg, time, addr, port; + var musicData, musicChanged, dict, newLedger, modelPath, musString, musFile, test1, test2, lastCurUID, commitType, commitPos, equalityLedger; + //msg.postln; + + /* + test1 = msg[1].asString.parseJSON; + test2 = (dir +/+ ".." +/+ "resources/tmp/tmp_music" ++ ".json").standardizePath.parseJSONFile; + msgInterpret.value(test1["music"])[0][0][0][1].class.postln; + msgInterpret.value(test2["music_data"])[0][0][0][1].class.postln; + (test1["music"] == test2["music_data"]).postln; + */ + + musicData = loadModelJSON.value(msg[1].asString.parseJSON)["music_data"].postln; + musicChanged = (musicData != seq).postln; + commitType = msg[2].asString; + commitPos = msg[3].postln.asInteger; + + lastCurUID = curUID.deepCopy; + curUID = genUID.value; + + File.mkdir((dir +/+ ".." +/+ "resources" +/+ curUID).standardizePath); + File.copy(exPath, (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_code" ++ ".scd").standardizePath); + + modelPath = (dir +/+ ".." +/+ "resources" +/+ curUID +/+ curUID ++ "_mus_model" ++ ".json").standardizePath; + dict = globalVarsToDict.value; + if(musicChanged, { + seq = musicData; + dict["music_data"] = seq; + dict["motif_edited"] = "true" + }); + dict["cur_uid"] = curUID; + + writeResources.value(modelPath, dict); + + File.delete(ledgerPath ++ "_bak"); + File.copy(ledgerPath, ledgerPath ++ "_bak"); + File.delete(ledgerPath); + + /* + if(commitType == "add", { + if(lastCurUID == "tmp", { + ledger = ledger.drop(-1).add(curUID); + }, { + ledger = ledger.add(curUID); + }) + }); + */ + + ledger.postln; + + if(commitType == "add", {ledger = ledger.add(curUID)}); + + if(commitType == "insert", {ledger = ledger.insert(commitPos + 1, curUID)}); + + if(commitType == "replace", {ledger = ledger.put(commitPos, curUID)}); + + equalityLedger = ledger.collect({arg item; item.asSymbol}); + if(equalityLedger.includes(\tmp).postln, {ledger.removeAt(equalityLedger.indexOf(\tmp).postln)}); + + ledger.postln; + + saveLedger.value(ledger, ledgerPath); + + addr.sendMsg("/committed", curUID, ledgerPath); + //refUID = curUID; + +}, \commit); + +OSCdef(\transport, {arg msg, time, addr, port; + msg.postln; + if(msg[1] == 0, { + group.set(\release, 2); + group.set(\gate, 0); + player.stop; + }, { + // the cued sequence can now be read from file, so this can be cleaned up + var cSize, patterns, pSeq, cuedSeek, indexStart, indexEnd, tmpLedger; + if(msg[1] == 1, { + pSeq = []; + cuedSeek = (seq != nil); + indexStart = msg[2].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.postln.parseJSON["music_data"]), path, indexStart + index, uid]); + file.close; + }); + }); + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + patterns = genPatterns.value(pSeq, addr); + }, { + pSeq = [loadModelJSON.value(msg[2].asString.parseJSON)["music_data"].postln]; + patterns = genPatterns.value(pSeq, addr, true); + }); + player = Pfset(pattern: patterns, cleanupFunc: { + addr.sendMsg("/transport", 0); + addr.sendMsg("/one_shot", 0); + }); + player = player.play + }); +}, \transport); + + +OSCdef(\transcribe_motif, {arg msg, time, addr, port; + var tSeq, refChord, refUID; + + msg.postln; + + tSeq = [loadModelJSON.value(msg[1].asString.parseJSON)["music_data"]]; + refUID = msg[2].asString.postln; + + if((refUID != "nil") && (refUID != "tmp"), { + var file; + file = File((dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }, { + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + }); + + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "lilypond" +/+ "includes").standardizePath, addr, "/transcribe_motif"); +}, \transcribe_motif); + + +OSCdef(\transcribe_all, {arg msg, time, addr, port; + var cSize, patterns, cuedSeek, indexStart, indexEnd, tmpLedger; + if(true, { + cuedSeek = (seq != nil); + indexStart = msg[1].asInteger; + indexEnd = ledger.size - if(cuedSeek, {2}, {1}); + + //tmp for testing transcription + indexEnd = (indexStart+5); + + //ledger.postln; + if(((indexStart == (ledger.size - 1)) && cuedSeek).not, { + var lilyPartLedgerFiles; + + lilyPartLedgerFiles = 4.collect({arg p; + File((dir +/+ ".." +/+ "lilypond" +/+ "includes" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath, "w"); + }); + + ledger[indexStart..indexEnd].do({arg uid, index; + var path, file, fileString, tSeq, refUID, refChord; + path = (dir +/+ ".." +/+ "resources" +/+ uid +/+ uid ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + fileString = file.readAllString; + tSeq = msgInterpret.value(fileString.parseJSON["music_data"]); + refUID = msgInterpret.value(fileString.parseJSON["ref_uid"]); + file.close; + + //uid.postln; + //(refUID == "nil").postln; + + refChord = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; + + if(refUID != "nil", { + path = (dir +/+ ".." +/+ "resources" +/+ refUID +/+ refUID ++ "_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + refChord = msgInterpret.value(file.readAllString.parseJSON["last_changes"]).last; + file.close; + }); + + if(index != indexEnd, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath); + }, { + ~transcribe.value(tSeq, refChord, (dir +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond").standardizePath, addr, "/transcribe_all"); + }); + + lilyPartLedgerFiles.do({arg f, p; + f.write("\\include \".." +/+ ".." +/+ "resources" +/+ uid +/+ "lilypond" +/+ "part_" ++ ["IV", "III", "II", "I"][p] ++ ".ly\"\n"); + }); + + }); + + lilyPartLedgerFiles.do({arg f; + f.close + }); + }); + /* + if(cuedSeek, { + var path, file; + path = (dir +/+ ".." +/+ "resources/tmp/tmp_mus_model" ++ ".json").standardizePath; + file = File(path, "r"); + pSeq = pSeq.add([msgInterpret.value(file.readAllString.parseJSON["music_data"]), path, ledger.size - 1, "tmp"]); + file.close; + }); + */ + }, { + + }); + +}, \transcribe_all); + +) + +~transcribe.value(~seq, dir); + +( +//synthdefs +~stringModelBusArray = 4.collect({Bus.audio(s, 1)}); +~sineBusArray = 4.collect({Bus.audio(s, 1)}); +~bassBusArray = 1.collect({Bus.audio(s, 1)}); +~hdustBusArray = 1.collect({Bus.audio(s, 1)}); +~samplerBusArray = 2.collect({Bus.audio(s, 1)}); +~sBuf = Buffer.alloc(s, 10, 2); +SynthDef(\string_model, {arg freq, gate = 1, sustain, amp, dur, attack, release = 1, busIndex = 0; + var trig, exc, sig1, sig2, noHarms; + noHarms = rrand(20, 40); + exc = Saw.ar(freq, TRand.ar(0.5, 1, Impulse.ar(freq))) * 0.001 + Dust.ar(10000, 0.01); + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(1, 2)}) ], exc) * 0.8).softclip; + //sig1 = HPF.ar(sig1, 300); + Out.ar(Select.kr(busIndex, ~stringModelBusArray), sig1 * amp * EnvGen.kr(Env.adsr(attack, 0.3, 0.9, release, 0.9, -3), gate, doneAction: 2)); + //Out.ar([0, 1], sig1 * EnvGen.kr(Env.asr(dur, 0.3, 1), gate, doneAction: 2)); +}).add; + +SynthDef(\sine, {arg freq, gate = 1, sustain, amp, dur, busIndex = 0; + var sig; + sig = SinOsc.ar(freq); + Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.asr(0.3, 0.4, 0.3), gate, timeScale: dur, doneAction: 2)); + //Out.ar(Select.kr(busIndex, ~sineBusArray), sig * EnvGen.kr(Env.sine(dur), gate, doneAction: 2)); +}).add; + +SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out; + var nameSpaces, sigs; + + sigs = [~stringModelBusArray, ~sineBusArray/*, ~bassBusArray, ~hdustBusArray, ~samplerBusArray*/].collect({arg busArray, i; + var nameSpace, sig; + nameSpace = ['string', 'sine', 'bass', 'hdust', 'sampler'][i]; + sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)}); + sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)}); + sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)}); + sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.1), 2); + }); + + sigs = Mix.ar(sigs / 4); + Out.ar(0, sigs) +}).add; + +SynthDef(\bass, { + var switches, drone; + switches = {|i| Dust.kr(0.1)} ! 9; + drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 / pow(harm, 2)); + SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; + Out.ar(~bassBusArray[0], Mix.new(drone) * 0.2); +}).add; + +SynthDef(\sampler, { + Out.ar(~samplerBusArray, PlayBuf.ar(2, ~sBuf, BufRateScale.kr(~sBuf), doneAction: 2)) +}).add; + +// main routine +SynthDef(\hdust, { + arg gate = 0; + var hierarchical_dust, low_sine, high_sine, brown_noise, white_noise; + // this triggers the combinations of sources + // it is similar to the Supercollider UGen called dust but with a hierarchical structure + hierarchical_dust = ( + TIRand.kr(0, 1, Impulse.kr(100)) * + TIRand.kr(0, 1, Impulse.kr(10)) * + TIRand.kr(0, 1, Impulse.kr(1)) * + TIRand.kr(0, 1, Impulse.kr(0.1)) + ); + // adjust the multiplier at the end of each line for adjusting levels + // note with each trigger, each source has a 1 in 3 chance of sounding + low_sine = SinOsc.ar(76.midicps / 16) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.1; + high_sine = SinOsc.ar(76.midicps * 8) * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.01; + brown_noise = BrownNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.025; + white_noise = WhiteNoise.ar() * (TIRand.kr(0, 2, hierarchical_dust) < 1) * 0.02; + Out.ar(~hdustBusArray[0], + ((low_sine + high_sine + brown_noise + white_noise) ) + ); +}).add; + +) + +( +var bass, hdust, sampler, mixer; +/* +bass = Synth.tail(~group, \bass); +hdust = Synth.tail(~group, \hdust); +sampler = Synth.head(~group, \sampler); +*/ +mixer = Synth.tail(~group, \mixer); + +OSCdef(\mixer, {arg msg, time, addr, port; + mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4]) +}, \mixer); + +/* +OSCdef(\sampler, {arg msg, time, addr, port; + msg.postln; + sampler.free; + ~sBuf.free; + ~sBuf = Buffer.read(s, msg[1].asString.postln, action: {sampler = Synth.head(~group, \sampler)}); +}, \sampler); +*/ +) + +/* old something +( +SynthDef(\test, {arg freq, gate = 1, sustain, amp, dur; + var trig, exc, sig1, sig2, noHarms, freqFinal, start, end; + noHarms = 30; + freq = WhiteNoise.ar * 3 + freq; + freqFinal = Duty.ar((1/freq), 0, freq); + trig = Changed.ar(freqFinal); + start = Demand.ar(trig, 0, Dwhite(-1, -0.75)); + end = Demand.ar(trig, 0, Dwhite(0.75, 1)); + exc = Phasor.ar(trig, (end - start) * freqFinal / SampleRate.ir, start, end, 0) * 0.001 + Dust.ar(10000, 0.01); + + sig1 = (Klank.ar(`[ Array.series(noHarms, freq, freq), + Array.geom(noHarms, 1, 0.2) + Array.fill(noHarms, {rrand(0.01, 0.03)}), + Array.fill(noHarms, {rrand(2, 3)}) ], exc) * 0.5).softclip; + sig1 = HPF.ar(sig1, 300); + Out.ar([0, 1], sig1 * EnvGen.kr(Env.adsr(0.3, 0.3, 0.9, 0.5, 0.9), gate, doneAction: 2)); +}).add; +) +*/ + diff --git a/resources/7ebbb471/7ebbb471_mus_model.json b/resources/7ebbb471/7ebbb471_mus_model.json new file mode 100644 index 0000000..0c12431 --- /dev/null +++ b/resources/7ebbb471/7ebbb471_mus_model.json @@ -0,0 +1,60 @@ +{ +"music_data": +[ + [ + [ + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0.625 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ], [ 1, 0, 0, -1, 1, 0 ] ], 0 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 2, -1, 0, -1, 0, 0 ] ], 1.875 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, -1, 0, -1, 2, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, -1, 1, -1, 1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 2, -1, 0, -2, 1, 0 ] ], 1.25 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 0, -1, 0, 0, 1, 0 ] ], 1.625 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ 2, -1, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.875 ] + ], + [ + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 2, -1, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, -1, 0, -1, 2, 0 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.125 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, 0, -1, -1, 1, 0 ], [ 1, 0, 0, -1, 1, 0 ] ], 0.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, -2, 0, -1, 1, 1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ 1, 0, 0, -1, 1, -1 ], [ 1, 0, 0, -1, 1, 0 ] ], 1.75 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ "Rest" ], [ 1, 0, 0, -1, 1, 0 ] ], 1.125 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ 2, -2, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ] ], 0.5 ], + [ [ [ 1, -1, 0, -1, 1, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1.25 ] + ] + ] +], +"last_changes": +[ + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] +], +"cur_uid": "7ebbb471", +"ref_uid": "nil", +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], +"step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], +"passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], +"hd_exp": 2, +"hd_invert": 0, +"order": +[ + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] +], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], +"passages_size": [ 0, 10 ], +"motif_edited": "true", +"order_edited": "false" +} \ No newline at end of file diff --git a/resources/7ebbb471/lilypond/part_I.ly b/resources/7ebbb471/lilypond/part_I.ly new file mode 100644 index 0000000..2bcfd04 --- /dev/null +++ b/resources/7ebbb471/lilypond/part_I.ly @@ -0,0 +1,24 @@ +{ + { r4 r16[ gis'8.^\markup { \pad-markup #0.2 "-18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ gis'2 ~ } + \bar "|" + { gis'8.[ g'16^\markup { \pad-markup #0.2 "+29"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↓" }}] ~ g'2. ~ } + \bar "|" + { g'8[ fis'8^\markup { \pad-markup #0.2 "+32"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↑" }}] ~ fis'2 ~ fis'8[ f'8^\markup { \pad-markup #0.2 "-33"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 5↑" }}] ~ } + \bar "|" + { f'4 dis'4^\markup { \pad-markup #0.2 "+12"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↓" }} ~ dis'4 ~ dis'8[ ais8^\markup { \pad-markup #0.2 "+49"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 7↑" }}] ~ } + \bar "|" \break + { ais2 ~ ais8.[ gis'16^\markup { \pad-markup #0.2 "-18"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↑" }}] ~ gis'4 ~ } + \bar "|" + { gis'1 ~ } + \bar "|" + { gis'1 ~ } + \bar "|" + { gis'1 ~ } + \bar "|" \break + { gis'1 ~ } + \bar "|" + { gis'1 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/7ebbb471/lilypond/part_II.ly b/resources/7ebbb471/lilypond/part_II.ly new file mode 100644 index 0000000..edf9d76 --- /dev/null +++ b/resources/7ebbb471/lilypond/part_II.ly @@ -0,0 +1,24 @@ +{ + { r4 r16[ e'8.^\markup { \pad-markup #0.2 "+40"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 13↓" }}] ~ e'2 ~ } + \bar "|" + { e'1 ~ } + \bar "|" + { e'1 ~ } + \bar "|" + { e'1 ~ } + \bar "|" \break + { e'1 ~ } + \bar "|" + { e'1 ~ } + \bar "|" + { e'8[ fis'8^\markup { \pad-markup #0.2 "+32"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 11↑" }}] ~ fis'4 ~ fis'8.[ e'16^\markup { \pad-markup #0.2 "-4"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 5↓" }}] ~ e'4 ~ } + \bar "|" + { e'16[ d'8.^\markup { \pad-markup #0.2 "+31"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 11↓" }}] ~ d'2 ~ d'16[ d'8.^\markup { \pad-markup #0.2 "+19"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "III"\normal-size-super " 13↑" }}] ~ } + \bar "|" \break + { d'2 ~ d'16[ b8.^\markup { \pad-markup #0.2 "+42"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "I"\normal-size-super " 13↓" }}] ~ b4 ~ } + \bar "|" + { b4 ~ b8.[ r16] r2 } + \bar "|" + { r1 } +\bar "||" +} \ No newline at end of file diff --git a/resources/7ebbb471/lilypond/part_III.ly b/resources/7ebbb471/lilypond/part_III.ly new file mode 100644 index 0000000..6b17e60 --- /dev/null +++ b/resources/7ebbb471/lilypond/part_III.ly @@ -0,0 +1,24 @@ +{ + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" + { r1 } + \bar "|" \break + { r1 } + \bar "|" + { r2 r8[ fis'8^\markup { \pad-markup #0.2 "-21"}_\markup { \lower #3 \pad-markup #0.2 \concat{ "IV"\normal-size-super " 3↓" }}] ~ fis'4 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" \break + { fis'1 ~ } + \bar "|" + { fis'1 ~ } + \bar "|" + { fis'4 r2. } +\bar "||" +} \ No newline at end of file diff --git a/resources/7ebbb471/lilypond/part_IV.ly b/resources/7ebbb471/lilypond/part_IV.ly new file mode 100644 index 0000000..cb68938 --- /dev/null +++ b/resources/7ebbb471/lilypond/part_IV.ly @@ -0,0 +1,24 @@ +{ + { cis'4^\markup { \pad-markup #0.2 "-19"} ~ cis'2. ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" \break + { cis'1 ~ } + \bar "|" + { cis'1 ~ } + \bar "|" + { cis'4 r2. } +\bar "||" +} \ No newline at end of file diff --git a/resources/piece_ledger_sq1_candidates.json b/resources/piece_ledger_sq1_candidates.json index ac51aff..dbf6200 100644 --- a/resources/piece_ledger_sq1_candidates.json +++ b/resources/piece_ledger_sq1_candidates.json @@ -2,7 +2,8 @@ "ledger": [ "314s49e1", - "600e3005", + "4a8a6e53", + "66f6a618", "4c01589b", "7e170ef8", "7ac10d34", diff --git a/resources/piece_ledger_sq1_candidates.json_bak b/resources/piece_ledger_sq1_candidates.json_bak new file mode 100644 index 0000000..9f9e86f --- /dev/null +++ b/resources/piece_ledger_sq1_candidates.json_bak @@ -0,0 +1,13 @@ +{ +"ledger": +[ + "314s49e1", + "4a8a6e53", + "4c01589b", + "7e170ef8", + "7ac10d34", + "628d5c8b", + "6abf27d4", + "535cc132" +] +} \ No newline at end of file diff --git a/resources/piece_ledger_sq1_candidates_stitch.json b/resources/piece_ledger_sq1_candidates_stitch.json new file mode 100644 index 0000000..d231aa7 --- /dev/null +++ b/resources/piece_ledger_sq1_candidates_stitch.json @@ -0,0 +1,11 @@ +{ +"ledger": +[ + "4a8a6e53", + "66f6a618", + "490b1e6e", + "46985d14", + "761e4585", + "6fb60ab6" +] +} \ No newline at end of file diff --git a/resources/piece_ledger_sq1_candidates_stitch.json_bak b/resources/piece_ledger_sq1_candidates_stitch.json_bak new file mode 100644 index 0000000..2166901 --- /dev/null +++ b/resources/piece_ledger_sq1_candidates_stitch.json_bak @@ -0,0 +1,11 @@ +{ +"ledger": +[ + "4a8a6e53", + "66f6a618", + "490b1e6e", + "46985d14", + "761e4585", + "7ebbb471" +] +} \ No newline at end of file diff --git a/resources/tmp/tmp_mus_model.json b/resources/tmp/tmp_mus_model.json index 1390f64..34af7a5 100644 --- a/resources/tmp/tmp_mus_model.json +++ b/resources/tmp/tmp_mus_model.json @@ -3,81 +3,43 @@ [ [ [ - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0 ], - [ [ [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 3.25 ], - [ [ [ 1, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.375 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 7.625 ] - ], - [ - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.125 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 1, 0, 0, -1, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.125 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 5.625 ] - ], - [ - [ [ [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0 ], - [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 8.5 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ -1, 0, 0, 0, 1, 0 ] ], 0.25 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ] ], 0.125 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, -1, 0, 0 ] ], 0.25 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 1, 0, 0, 0, 0 ] ], 0.375 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 2, 0, 0, -1, 0, 0 ] ], 0.25 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 1, 0, 0, 0 ] ], 0.125 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 0, -1, 0 ] ], 0.125 ], - [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], 3.75 ] - ], - [ - [ [ [ 1, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], 0.25 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], 0.875 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 0, 1 ], [ -1, 1, 0, 0, 0, 0 ] ], 0.25 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 2, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], 0.125 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 1, 0, 0, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], 0.25 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ 0, 1, 0, -1, 0, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], 7.125 ] - ], - [ - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], 0 ], - [ [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0 ], - [ [ [ 0, 1, 0, 0, 0, 1 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], 0.125 ], - [ [ [ 0, 1, 0, 0, 0, 1 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 1, 1, 0, -1, 0, 0 ] ], 0 ], - [ [ [ 2, 1, 0, -1, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 1, 1, 0, -1, 0, 0 ] ], 4.125 ], - [ [ [ 2, 1, 0, -1, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ "Rest" ] ], 0.375 ], - [ [ [ 2, 1, 0, -1, 0, 0 ], [ "Rest" ], [ -1, 1, 0, 0, 1, 0 ], [ "Rest" ] ], 0 ], - [ [ [ 2, 1, 0, -1, 0, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], - [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 15.125 ] + [ [ [ "Rest" ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 9.625 ], + [ [ [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 0.75 ], + [ [ [ 0, 0, -1, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 1.5 ], + [ [ [ -1, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 1.75 ], + [ [ [ 0, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ] ], 5.25 ], + [ [ [ 0, -1, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 9.125 ] ] ] ], "last_changes": [ - [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ -1, 1, 0, 0, 0, 0 ] ], - [ [ 1, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], - [ [ 0, 1, 0, 0, 0, 1 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], - [ [ 0, 1, 0, 0, 0, 1 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 1, 1, 0, -1, 0, 0 ] ], - [ [ 2, 1, 0, -1, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 1, 0 ], [ 1, 1, 0, -1, 0, 0 ] ] + [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, 0, -1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ], + [ [ 0, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ] ], "cur_uid": "tmp", "ref_uid": "nil", -"order_seed": 588436, -"dur_seed": 661239, -"motifs_seed": 733875, -"entrances_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.26424870466321, 0.75675675675676, 0.5, 0.5, 0.58549222797927, 0.72635135135135, 1, 0.5 ], -"passages_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], -"exits_probs_vals": [ 0.75, 0, 10, 0, 0.5, 0.20725388601036, 0.68581081081081, 0.24093264248705, 0.34121621621622, 0.5, 0.5, 0.67616580310881, 0.81081081081081, 1, 0.5 ], -"ranges": [ [ -384, 2400 ], [ -507, 2400 ], [ -282, 2237 ], [ -1200, 2053 ] ], +"order_seed": 209164, +"dur_seed": 417909, +"motifs_seed": 885208, +"entrances_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"passages_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"exits_probs_vals": [ 0.34, 0, 10, 0.5, 2, 0, 0.5, 0.24509803921569, 0.89189189189189, 0.5, 1, 0.5, 0.5, 0.8562091503268, 0.69932432432432, 1, 0.5 ], +"ranges": [ [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ], [ -1200, 2400 ] ], "step_probs_vals": [ 0, 1200, 0, 0.5, 0.5, 0.5, 1, 0.5 ], "passages_weights": [ 0.75, 0.75, 0.75, 0.75, 0.75 ], "hd_exp": 2, "hd_invert": 0, "order": [ - [ [ 2, 1 ], [ 0, 0 ], [ 3 ] ], - [ [ 0 ], [ 1, 1 ], [ 3, 2 ] ], - [ [ 2 ], [ 3, 3, 3, 3, 3, 3, 3, 3 ], [ 0, 1 ] ], - [ [ 3, 0, 1 ], [ 2, 2, 2, 2 ], [ ] ], - [ [ 1 ], [ 2, 3, 0, 3, 0 ], [ ] ] + [ [ 2 ], [ 0, 0, 0, 0 ], [ 1, 3 ] ] ], -"sus_weights": [ 0.75, 0.69, 0.75 ], -"order_size": [ 2, 6 ], +"sus_weights": [ 0.75, 0.75, 0.75 ], +"order_size": [ 1, 10 ], "passages_size": [ 0, 10 ], "motif_edited": "false", "order_edited": "false" diff --git a/supercollider/material_tweak.scd b/supercollider/material_tweak.scd new file mode 100644 index 0000000..e56785b --- /dev/null +++ b/supercollider/material_tweak.scd @@ -0,0 +1,99 @@ +[ "314s49e1", "tmp" ].collect({arg item; item.asSymbol}).indexOf(\tmp) + +( + +var stringifyToDepth, transpose, swap; + +stringifyToDepth = {arg data, maxDepth = 1; + var prettyString = "", rCount = 0, writeArray, indent; + + if(maxDepth == 0, { + data.asCompileString + }, { + indent = {arg size; size.collect({" "}).join("")}; + writeArray = {arg array; + prettyString = prettyString ++ indent.value(rCount) ++ "[\n"; + rCount = rCount + 1; + if(rCount < maxDepth, { + array.do({arg subArray; writeArray.value(subArray)}); + }, { + prettyString = prettyString ++ array.collect({arg subArray; + indent.value(rCount + 1) ++ subArray.asCompileString + }).join(",\n"); + }); + rCount = rCount - 1; + prettyString = prettyString ++ "\n" ++ indent.value(rCount) ++ "],\n"; + }; + + writeArray.value(data); + prettyString.replace(",\n\n", "\n").drop(-2); + }) +}; + +transpose = {arg data, trans; + var res; + res = data.collect({arg x; + x.collect({arg y; + y.collect({arg z; + [z[0].collect({arg item; if(item == ["Rest"], {item}, {item + trans })}), z[1]]})})}); +}; + +swap = {arg data, swaplist; + var res; + res = data.collect({arg x; + x.collect({arg y; + y.collect({arg z; + var cur; + cur = z[0]; + [ + swaplist.do({arg curSwap; curSwap.postln; cur = cur.postln.swap(curSwap[0], curSwap[1])}); cur, + z[1] + ] + }) + }) + }); +}; + +c = [ + [ + [ + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0.625 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ] ], 0 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 1, 0, 0, 0, 0 ] ], 1.75 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 1, 0, 0, 0, -1, 0 ] ], 1.875 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 1, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 0, 1, 0, 0, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 1, 0, 0, -1, 0, 0 ] ], 1.25 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 1, 0, 0 ] ], 1.625 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 1, 0, 0, 0, 0 ] ], 1.875 ] + ], + [ + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 1, 0, 0, 0, 0, -1 ], [ 0, 1, 0, 0, 0, 0 ] ], 1 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0 ], [ 0, 1, 0, 0, 0, 0 ] ], 1.125 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 1, -1, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0 ] ], 0.75 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 1, 0, 0, -1, 0 ], [ 0, 1, 0, 0, 0, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 0, 1 ], [ 0, 1, 0, 0, 0, 0 ] ], 1.5 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, -1 ], [ 0, 1, 0, 0, 0, 0 ] ], 1.75 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ 0, 1, 0, 0, 0, 0 ] ], 1.125 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ] ], 0.5 ], + [ [ [ 0, 0, 0, 0, 0, 0 ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 0 ], + [ [ [ "Rest" ], [ "Rest" ], [ "Rest" ], [ "Rest" ] ], 1.25 ] + ] + ] +]; + +//c = c.collect({arg x; x.collect({arg y; y.collect({arg z; [z[0].collect({arg item; if(item == ["Rest"], {item}, {item + [ 1, -1, 0, 0, -1, 0 ] })}), z[1]]})})}); +c = transpose.value(c, [ 1, -1, 0, -1, 1, 0 ]); +//c = swap.value(c, [[2, 3]]); + +f = File("tweak.txt", "w"); + +f.write(stringifyToDepth.value(c, 3)); +f.close; +//c.do({arg x; "[".postln; x.do({arg y; "[".postln; y.do({arg z; [z[0], z[1]].post; ",".postln}); "],".postln}); "]".postln}) +//c.do({arg x; "[".postln; x.do({arg y; "[".postln; y.do({arg z; [z[0].collect({arg item; if(item == ["Rest"], {item}, {item + [ 0, -1, 0, 0, -1, 1 ]})}), z[1]].post; ",".postln}); "],".postln}); "]".postln}) +) + +[ 0, -1, 0, 0, -1, 1 ] + +[ -2, 0, 0, 1, 1, 0 ] \ No newline at end of file