{"id":21125945,"url":"https://github.com/shimpe/mitola","last_synced_at":"2025-10-26T01:37:00.782Z","repository":{"id":184544323,"uuid":"672078315","full_name":"shimpe/mitola","owner":"shimpe","description":"supercollider quark for microtonal music composition; microtonal counterpart to panola","archived":false,"fork":false,"pushed_at":"2024-10-02T20:45:19.000Z","size":1226,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-19T18:45:21.958Z","etag":null,"topics":["microtonal","supercollider","supercollider-quark","tuning","xenharmony"],"latest_commit_sha":null,"homepage":"","language":"SuperCollider","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shimpe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-28T21:39:39.000Z","updated_at":"2024-10-02T20:45:23.000Z","dependencies_parsed_at":"2025-01-21T05:51:09.512Z","dependency_job_id":null,"html_url":"https://github.com/shimpe/mitola","commit_stats":null,"previous_names":["shimpe/mitola"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/shimpe/mitola","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shimpe%2Fmitola","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shimpe%2Fmitola/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shimpe%2Fmitola/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shimpe%2Fmitola/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shimpe","download_url":"https://codeload.github.com/shimpe/mitola/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shimpe%2Fmitola/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281047794,"owners_count":26435124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["microtonal","supercollider","supercollider-quark","tuning","xenharmony"],"created_at":"2024-11-20T04:38:35.719Z","updated_at":"2025-10-26T01:37:00.767Z","avatar_url":"https://github.com/shimpe.png","language":"SuperCollider","funding_links":[],"categories":[],"sub_categories":[],"readme":"![logo](https://github.com/shimpe/mitola/blob/main/HelpSource/logo/mitola.png?raw=true)\n\n# mitola \n**MI**cro **TO**nal **LA**nguage for supercollider; microtonal counterpart to Panola\n \nMitola provides notation and utilities to write microtonal music in supercollider using a text based notation. Mitola is modeled after its sister quark Panola, which provides text based notation for \"conventional\" music.\nMitola shares many ideas and syntax with Panola, but differs in some aspects where I thought Panola could be improved, or where things cannot be reused as-is.\n\n## installation\n\nMitola depends on another quark called scparco which is a library to generate parsers in supercollider. Both need to be installed:\n\n```smalltalk\n(\nQuarks.install(\"https://github.com/shimpe/mitola\"); // mitola implementation -\u003e need version 0.0.4 or newer\n// this should automatically also install the dependency scparco\n)\n```\n\n## degrees\n\nA Mitola score consists of a list of notes and/or chords. Mitola does not use note names. Instead it uses integer degrees 1,2,...N, where N is determined by the tuning in use. Suppose you have a 12EDO tuning, then N can be at most 12.\n\n```smalltalk\n(\nvar valid_notes_12_edo = \"1 2 3 4 5 6 7 8 9 10 11 12\"; // 1-based degree names\n)\n```\n\n## score degrees vs scala degrees\n\nWhat do to if the scala file defines more degrees than you want to use in your composition? (E.g. you want to select 12 out of 17) In such cases you have the choice to:\n1. either make sure only to use the correct degree numbers, or\n2. set up a degree mapping from score degrees to scala degrees\n\nTo set up a degree mapping, you can pass to Mitola a ```degree_mapping``` argument. This ```degree_mapping``` should be a ```Dictionary``` containing a mapping from score degree (1-based Integer) to scala degree (1-based integer). \n\nNote that pitch modifiers (see next) calculate with score degrees, not with scala degrees. This may be important to understand the behavior in cases where the distance between different score degrees is different.\n\n\n## degree modifiers\n\nDegrees can be modified with modifiers (in conventional music these would be accidentals, like sharps and flats). Modifiers in Mitola can be specified in two ways. First way: absolute modification by a number of cents. E.g. 1{+100.0} modifies degree 1 by adding 100.0 cents. Note that you must include a decimal point for the number to be interpreted as cents. Similarly 4{-53.0} would lower the pitch represented by degree 4 by 53.0 cents. \n\nThe second way is to specify a ratio. E.g. 5{+2/3} will modify the pitch so that it sounds 2/3 between degree 5 and degree 6. Ratios with an abs value larger than 1 are reduced: 5{-4/3} is interpreted as 4{-1/3}. This is important to understand the behavior in case of tunings with different gaps between the different degrees. An alternative way to specify ratios is using prime vector notation. In prime vector notation, a ratio is defined as a multiplication of prime factors. The exponents of the prime factors are between | and \u003e. So this is a valid note: 3{+|1/12\u003e}. It will raise degree 3 with 2^(1/12).\n\n```smalltalk\n(\nvar modified_notes = \"7{+50.0} 4{-34.0} 6{+1/4} 10{-3/4} 3{+|1/2 -3/2 2/7\u003e}\";\n)\n```\n\nIn addition to modifiers, degrees also have an equivalence interval (aka equave). In traditional notation, this would be called an octave. The equivalence interval is indicated using square brackets. E.g. 1[4] is degree one in the fourth equave. If you do not specify an equave for a degree, the previous one is reused.\n\n```smalltalk\n(\nvar same_note_in_different_equaves = \"3[2] 3[4] 3[5]\";\n)\n```\n\n## rhythm\n\nTo indicate rhythm, a note can be decorated with an underscore (_) followed by a number, e.g. 1_4 indicates degree one as a quarter note (aka crotchet), whereas 1_8 is the first degree as an eighth note (aka quaver). If you do not specify a duration value, the last specified one is reused.\n\n```smalltalk\n(\nvar quarter_note_followed_by_two_eighths = \"1_4 5_8 5\";\n)\n```\n\nThe duration value optionally can be extended with one or more dots. Similar to traditional notation, 1_4 lasts one quarter note; 1_4. lasts 1+0.5 quarter notes (= 3 eighths) and 1_4.. lasts 1+0.5+0.25 quarter notes (=7 sixteenths (semi quavers))).\n\n```smalltalk\n(\nvar two_dotted_quarter_note_followed_by_a_regular_eighth = \"6_4. 7 9_8\";\n)\n```\n\nIn addition to the dots, one can also (optionally) specificy a multiplier and a divider. These can be used to define tuplets. E.g. \"1[4]_8*2/3 4 7\" consists of 3 degrees in the fourth equave forming a triplet of eighth notes.\n\n```smalltalk\n(\nvar triplet_of_eighths = \"1[4]_8*2/3 4 7\";\n)\n```\n\nNotes can also optionally be decorated with properties (anything you like really). These properties by default are included in the supercollider patterns that are derived from the score, where they can be used to drive synth arguments or control behavior of the patterns. The properties can be animated between notes (see example code below). A property's value written in curly brackets ```{ }``` will cause the value to linearly interpolate to the next occurrence of that property's value. If the value is written in square brackets ```[ ]```  instead, the value remains constant until the next occurrence of that property's value.\n\n```smalltalk\n(\nvar animated_prop_crescendo = \"1[4]@amp{0.2} 2 3 4 5 6 7@amp{0.9}\";\nvar static_prop_two_staccato_one_legato = \"1[4]@legato[0.1] 5 1[5]@legato[1.0];\n)\n```\n\n## making chords\n\nNotes can be grouped in angular brackets \u003c \u003e to make chords. Limitation: the properties of the first note iin the chord are used for the complete chord. \"\u003c 1[4]_8 4 1{+25.0}[5]\u003e\" is a chord consisting of degrees 1[4] 4[4] and 1{+25.0}[5].\n\n```smalltalk\n(\nvar chord = \"\u003c1[4]_2 4 7\u003e\";\n)\n```\n\n## repeats \n\nLastly, notes can be put between repeat brackets and the number of repeats can be indicated, e.g.\n|: 1[4]_8 2 3 :|*5 repeats notes 1[4]_8 2 3 five times.\n\n```smalltalk\n(\nvar repeats = \"|: 1[4]_8 2 3 :| * 5\";\n)\n```\n\n## tuning\n\nTo convert Mitola scores to frequencies it is necessary to know in which tuning the score has to be interpreted. Tuning is indicated in the form of a scala definition and a root frequency. In order to pin a given degree in your scala definition to a fixed frequency (e.g. ensure that A4 is 440Hz in a 12EDO tuning), a suitable root_frequency can be calculated using the MtlRootFrequencyCalculator class.\n\n\n# Examples\n```smalltalk\n// Let's start with the \"Hello world\" of Mitola: a simple scale.\n// In microtonal music, we need to define the scale degrees first. I'm here inlining the scala contents,\n// but you can also load them from file by passing a scala_filename instead scala_contents.\n(\ns.waitForBoot({\n\tvar tuning = [\n\t\t\"! 12edo.scl\",\n\t\t\"!\",\n\t\t\"12 edo\",\n\t\t\" 12\",\n\t\t\"!\",\n\t\t\" | 1/12 \u003e\",\n\t\t\" | 2/12 \u003e\",\n\t\t\" | 3/12 \u003e\",\n\t\t\" | 4/12 \u003e\",\n\t\t\" | 5/12 \u003e\",\n\t\t\" | 6/12 \u003e\",\n\t\t\" | 7/12 \u003e\",\n\t\t\" | 8/12 \u003e\",\n\t\t\" | 9/12 \u003e\",\n\t\t\" | 10/12 \u003e\",\n\t\t\" | 11/12 \u003e\",\n\t\t\" 2/1\"\n\t].join(\"\\n\");\n\tvar score = MtlMitola(\"1[4]_16@amp[0.6] 2 3 4 5 6 7 8 9 10 11 12 1[5]\", scala_contents: tuning);\n\t// find out which root frequency to use to get degree 10 in octave 4 (A4) to map to 440Hz.\n\tvar r = MtlRootFrequencyCalculator(tuning);\n\tvar root_freq = r.get_root_frequency(\"10[4]\", 440);\n\tvar player = score.as_pbind(root_frequency:root_freq).play; // listen to the score with the default instrument\n});\n)\n```\n\nThen a diatonic major scale selected from a 12EDO tuning by using note mapping.\n```smalltalk\n(\ns.waitForBoot({\n\tvar scala = [\n\t\t\"! 12EDO.scl\",\n\t\t\"!\",\n\t\t\"12 EDO\",\n\t\t\" 12\",\n\t\t\"!\",\n\t\t\" | 1/12 \u003e\",\n\t\t\" | 2/12 \u003e\",\n\t\t\" | 3/12 \u003e\",\n\t\t\" | 4/12 \u003e\",\n\t\t\" | 5/12 \u003e\",\n\t\t\" | 6/12 \u003e\",\n\t\t\" | 7/12 \u003e\",\n\t\t\" | 8/12 \u003e\",\n\t\t\" | 9/12 \u003e\",\n\t\t\" | 10/12 \u003e\",\n\t\t\" | 11/12 \u003e\",\n\t\t\" 2/1\"\n\t].join(\"\\n\");\n\tvar m = MtlMitola(\"1[4]_16 2 3 4 5 6 7 1[5]\",\n\t\tscala_contents:scala,\n\t\tdegree_mapping:Dictionary[1-\u003e1, 2-\u003e3, 3-\u003e5, 4-\u003e6, 5-\u003e8, 6-\u003e10, 7-\u003e12]);\n\tvar r = MtlRootFrequencyCalculator(scala_contents:scala, degree_mapper:m.degree_mapper);\n\tvar root_freq = r.get_root_frequency(\"6[4]\", 440); // a4 to 440Hz (6 is a one-based score degree, not a scala degree!)\n\tvar pattern = m.as_pbind(root_frequency:root_freq);\n\tvar player = pattern.play;\n});\n)\n```\n\nAnother example of playing two lines simultaneously in 7EDO tuning \n\u003e (nothing prevents you from writing the second line in a different tuning of course...)\n\n```smalltalk\n(\ns.waitForBoot({\n\tvar tuning = [\n\t\t\"! 7edo.scl\",\n\t\t\"!\",\n\t\t\"7 edo\",\n\t\t\" 7\",\n\t\t\"!\",\n\t\t\" | 1/7 \u003e\",\n\t\t\" | 2/7 \u003e\",\n\t\t\" | 3/7 \u003e\",\n\t\t\" | 4/7 \u003e\",\n\t\t\" | 5/7 \u003e\",\n\t\t\" | 6/7 \u003e\",\n\t\t\" 2/1\"\n\t].join(\"\\n\");\n\tvar score = MtlMitola(\"1[4]_16@amp[0.6] 2 3_32 4 5_16 6 7 1[5]_4\", scala_contents: tuning);\n\tvar score2 = MtlMitola(\"1[3]_16@amp[0.6] 5 1 5 1 5 1 5 1\", scala_contents:tuning);\n\t// find out which root frequency to use to get degree 4 in octave 4 to map to 432Hz.\n\tvar r = MtlRootFrequencyCalculator(tuning);\n\tvar root_freq = r.get_root_frequency(\"4[4]\", 432);\n\tvar player = Ppar([\n\t\tscore.as_pbind(root_frequency:root_freq),\n\t\tscore2.as_pbind(root_frequency:root_freq)\n\t]).play; // listen to score and score2 simultaneously with the default instrument\n});\n)\n```\n\nIf you define synths in supercollider, you can use them instead of the default instrument to play\n\n```smalltalk\n(\ns.waitForBoot({\n\tvar tuning = [\n\t\t\"! 7edo.scl\",\n\t\t\"!\",\n\t\t\"7 edo\",\n\t\t\" 7\",\n\t\t\"!\",\n\t\t\" | 1/7 \u003e\",\n\t\t\" | 2/7 \u003e\",\n\t\t\" | 3/7 \u003e\",\n\t\t\" | 4/7 \u003e\",\n\t\t\" | 5/7 \u003e\",\n\t\t\" | 6/7 \u003e\",\n\t\t\" 2/1\"\n\t].join(\"\\n\");\n\tvar score = MtlMitola(\"1[4]_16@amp[0.6] 2 3_32 4 5_16 6 7 1[5]_4\", scala_contents: tuning);\n\tvar score2 = MtlMitola(\"1[3]_16@amp[0.6] 5 1 5 1 5 1 5 1\", scala_contents:tuning);\n\t// find out which root frequency to use to get degree 4 in octave 4 to map to 432Hz.\n\tvar r = MtlRootFrequencyCalculator(tuning);\n\tvar root_freq = r.get_root_frequency(\"4[4]\", 432);\n\tvar pattern = Ppar([\n\t\tscore.as_pbind(\\sawSynth, root_frequency:root_freq),\n\t\tscore2.as_pbind(\\sawSynth, root_frequency:root_freq)\n\t]);\n\tvar player;\n\n\tSynthDef(\\sawSynth, { arg freq = 440, amp = 0.1, att = 0.1, rel = 2, lofreq = 1000, hifreq = 3000, pan = 0;\n\t\tvar env, snd;\n\t\tenv = Env.perc(\n\t\t\tattackTime: att,\n\t\t\treleaseTime: rel,\n\t\t\tlevel: amp\n\t\t).kr(doneAction: 2);\n\t\tsnd = Saw.ar(freq: freq * [0.99, 1, 1.001, 1.008], mul: env);\n\t\tsnd = LPF.ar(\n\t\t\tin: snd,\n\t\t\tfreq: LFNoise2.kr(1).range(lofreq, hifreq)\n\t\t);\n\t\tsnd = Mix.ar(snd);\n\t\tsnd = Pan2.ar(snd, pan);\n\t\tOut.ar(0, snd);\n\t\t// Basic saw synth\n\t\t//By Bruno Ruviaro\n\t\t//http://sccode.org/1-54H\n\t}).add;\n\n\ts.sync;\n\n\tplayer = pattern.play;\n});\n)\n```\n\nUsing properties, we can now even change synth parameters. Wrapping property values in {} animates them in the notes between, so here we make a crescendo in the melody between amp=0.2 and amp=0.6. To understand better what happens: \"amp\" appears in the score as a property name, and in the SynthDef as an argument. This shared name is how the score is linked to the SynthDef.\n\nIf you wrap the property values in [], the values remain static, so in the accompaniment, the amplitude stays at 0.3, until the last note where it becomes 0.6.\n```smalltalk\n(\ns.waitForBoot({\n\tvar tuning = [\n\t\t\"! 7edo.scl\",\n\t\t\"!\",\n\t\t\"7 edo\",\n\t\t\" 7\",\n\t\t\"!\",\n\t\t\" | 1/7 \u003e\",\n\t\t\" | 2/7 \u003e\",\n\t\t\" | 3/7 \u003e\",\n\t\t\" | 4/7 \u003e\",\n\t\t\" | 5/7 \u003e\",\n\t\t\" | 6/7 \u003e\",\n\t\t\" 2/1\"\n\t].join(\"\\n\");\n\tvar score = MtlMitola(\"1[4]_16@amp{0.2} 2 3_32 4 5_16 6 7 1[5]_4@amp{0.6}\", scala_contents: tuning);\n\tvar score2 = MtlMitola(\"1[3]_16@amp[0.3] 5 1 5 1 5 1 5 1@amp[0.6]\", scala_contents:tuning);\n\t// find out which root frequency to use to get degree 4 in octave 4 to map to 432Hz.\n\tvar r = MtlRootFrequencyCalculator(tuning);\n\tvar root_freq = r.get_root_frequency(\"4[4]\", 432);\n\tvar pattern = Ppar([\n\t\tscore.as_pbind(\\sawSynth, root_frequency:root_freq),\n\t\tscore2.as_pbind(\\sawSynth, root_frequency:root_freq)\n\t]);\n\tvar player;\n\n\tSynthDef(\\sawSynth, { arg freq = 440, amp = 0.1, att = 0.1, rel = 2, lofreq = 1000, hifreq = 3000, pan = 0;\n\t\tvar env, snd;\n\t\tenv = Env.perc(\n\t\t\tattackTime: att,\n\t\t\treleaseTime: rel,\n\t\t\tlevel: amp\n\t\t).kr(doneAction: 2);\n\t\tsnd = Saw.ar(freq: freq * [0.99, 1, 1.001, 1.008], mul: env);\n\t\tsnd = LPF.ar(\n\t\t\tin: snd,\n\t\t\tfreq: LFNoise2.kr(1).range(lofreq, hifreq)\n\t\t);\n\t\tsnd = Mix.ar(snd);\n\t\tsnd = Pan2.ar(snd, pan);\n\t\tOut.ar(0, snd);\n\t\t// Basic saw synth\n\t\t//By Bruno Ruviaro\n\t\t//http://sccode.org/1-54H\n\t}).add;\n\n\ts.sync;\n\n\tplayer = pattern.play;\n});\n)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshimpe%2Fmitola","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshimpe%2Fmitola","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshimpe%2Fmitola/lists"}