{"id":85034,"date":"2026-07-26T22:40:44","date_gmt":"2026-07-26T14:40:44","guid":{"rendered":"https:\/\/www.wsisp.com\/helps\/85034.html"},"modified":"2026-07-26T22:40:44","modified_gmt":"2026-07-26T14:40:44","slug":"%e6%89%8b%e6%9c%ba%e7%ab%afpython%e8%af%ad%e8%a8%80%e4%bd%9c%e6%9b%b2%e8%bd%af%e4%bb%b6%e6%96%b0%e7%89%88%e4%bb%a3%e7%a0%81qzq","status":"publish","type":"post","link":"https:\/\/www.wsisp.com\/helps\/85034.html","title":{"rendered":"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ"},"content":{"rendered":"<p># -*- coding: utf-8 -*-<br \/>\n&#034;&#034;&#034;<br \/>\n\u4f5c\u66f2\u8f6f\u4ef6 v3 &#8211; \u6574\u5408\u7ecf\u5178\u4f5c\u66f2\u6280\u672f<br \/>\n\u652f\u6301&#xff1a;\u5bf9\u4f4d\u6cd5\u3001\u548c\u58f0\u5b66\u3001\u66f2\u5f0f\u5b66\u3001\u914d\u5668\u6cd5\u3001\u73b0\u4ee3\u6280\u6cd5<br \/>\n\u8f93\u51fa&#xff1a;\u591a\u58f0\u90e8\u7b80\u8c31 &#043; MIDI\u591a\u8f68<br \/>\n&#034;&#034;&#034;<\/p>\n<p>import random<br \/>\nimport math<br \/>\nimport tkinter as tk<br \/>\nfrom tkinter import ttk, scrolledtext, messagebox, filedialog<br \/>\nfrom threading import Thread<\/p>\n<p># &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061; \u57fa\u7840\u6570\u636e\u5c42 &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;<\/p>\n<p># \u8c03\u5f0f\u4e0e\u97f3\u9636<br \/>\nKEY_OFFSET &#061; {<br \/>\n    &#034;C\u5927\u8c03&#034;: 0, &#034;C#\u5927\u8c03&#034;: 1, &#034;Db\u5927\u8c03&#034;: 1,<br \/>\n    &#034;D\u5927\u8c03&#034;: 2, &#034;D#\u5927\u8c03&#034;: 3, &#034;Eb\u5927\u8c03&#034;: 3,<br \/>\n    &#034;E\u5927\u8c03&#034;: 4, &#034;F\u5927\u8c03&#034;: 5, &#034;F#\u5927\u8c03&#034;: 6, &#034;Gb\u5927\u8c03&#034;: 6,<br \/>\n    &#034;G\u5927\u8c03&#034;: 7, &#034;G#\u5927\u8c03&#034;: 8, &#034;Ab\u5927\u8c03&#034;: 8,<br \/>\n    &#034;A\u5927\u8c03&#034;: 9, &#034;A#\u5927\u8c03&#034;: 10, &#034;Bb\u5927\u8c03&#034;: 10,<br \/>\n    &#034;B\u5927\u8c03&#034;: 11,<br \/>\n    &#034;A\u5c0f\u8c03&#034;: 9, &#034;E\u5c0f\u8c03&#034;: 4, &#034;D\u5c0f\u8c03&#034;: 2,<br \/>\n    &#034;G\u5c0f\u8c03&#034;: 7, &#034;C\u5c0f\u8c03&#034;: 0, &#034;F\u5c0f\u8c03&#034;: 5,<br \/>\n    &#034;Bb\u5c0f\u8c03&#034;: 10, &#034;Eb\u5c0f\u8c03&#034;: 3,<br \/>\n}<\/p>\n<p># \u97f3\u9636\u7c7b\u578b<br \/>\nMAJOR_SCALE &#061; [0, 2, 4, 5, 7, 9, 11]<br \/>\nMINOR_SCALE &#061; [0, 2, 3, 5, 7, 8, 10]<br \/>\nHARMONIC_MINOR &#061; [0, 2, 3, 5, 7, 8, 11]<br \/>\nMELODIC_MINOR_UP &#061; [0, 2, 3, 5, 7, 9, 11]<br \/>\nPENTATONIC_MAJOR &#061; [0, 2, 4, 7, 9]<br \/>\nPENTATONIC_MINOR &#061; [0, 3, 5, 7, 10]<br \/>\nBLUES_SCALE &#061; [0, 3, 5, 6, 7, 10]<br \/>\nWHOLE_TONE &#061; [0, 2, 4, 6, 8, 10]<br \/>\nOCTATONIC &#061; [0, 1, 3, 4, 6, 7, 9, 10]<br \/>\nALL_SEMITONES &#061; list(range(12))<\/p>\n<p># \u534a\u97f3 \u2192 \u7b80\u8c31<br \/>\nSEMI_MAP &#061; {<br \/>\n    0: &#034;1&#034;, 1: &#034;1#&#034;, 2: &#034;2&#034;, 3: &#034;2#&#034;,<br \/>\n    4: &#034;3&#034;, 5: &#034;4&#034;, 6: &#034;4#&#034;, 7: &#034;5&#034;,<br \/>\n    8: &#034;5#&#034;, 9: &#034;6&#034;, 10: &#034;6#&#034;, 11: &#034;7&#034;,<br \/>\n}<\/p>\n<p># \u7b80\u8c31 \u2192 MIDI\u97f3\u9ad8&#xff08;C4&#061;60&#xff0c;\u5373\u7b80\u8c311&#061;C&#xff09;<br \/>\ndef jianpu_to_midi(note_str, base_octave&#061;4):<br \/>\n    &#034;&#034;&#034;\u5c06\u7b80\u8c31\u5b57\u7b26\u4e32\u8f6c\u4e3aMIDI\u97f3\u9ad8\u6570\u5b57&#034;&#034;&#034;<br \/>\n    if not note_str or note_str in [&#039;|&#039;, &#039; &#039;, &#039;-&#039;, &#039;_&#039;]:<br \/>\n        return None<\/p>\n<p>    num &#061; None<br \/>\n    sharp &#061; 0<br \/>\n    oct_shift &#061; 0<\/p>\n<p>    for ch in note_str:<br \/>\n        if ch in &#039;1234567&#039;:<br \/>\n            num &#061; int(ch)<br \/>\n        elif ch &#061;&#061; &#039;#&#039;:<br \/>\n            sharp &#061; 1<br \/>\n        elif ch &#061;&#061; &#034;&#039;&#034;:<br \/>\n            oct_shift &#043;&#061; 1<br \/>\n        elif ch &#061;&#061; &#039;.&#039;:<br \/>\n            oct_shift -&#061; 1<\/p>\n<p>    if num is None:<br \/>\n        return None<\/p>\n<p>    num_to_semi &#061; {1: 0, 2: 2, 3: 4, 4: 5, 5: 7, 6: 9, 7: 11}<br \/>\n    semi &#061; num_to_semi[num] &#043; sharp<br \/>\n    midi &#061; (base_octave &#043; oct_shift) * 12 &#043; 12 &#043; semi<br \/>\n    return midi<\/p>\n<p># &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061; \u98ce\u683c\u914d\u7f6e &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;<\/p>\n<p>STYLES &#061; {<br \/>\n    &#034;\u6b22\u5feb&#034;: {&#034;scale&#034;: MAJOR_SCALE, &#034;rhythms&#034;: [0.25, 0.5, 0.5, 0.25, 1.0, 0.5],<br \/>\n             &#034;step_pref&#034;: 2, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.2},<br \/>\n    &#034;\u6292\u60c5&#034;: {&#034;scale&#034;: MAJOR_SCALE, &#034;rhythms&#034;: [0.5, 1.0, 1.0, 0.5, 2.0],<br \/>\n             &#034;step_pref&#034;: 1, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.1},<br \/>\n    &#034;\u6fc0\u6602&#034;: {&#034;scale&#034;: MAJOR_SCALE, &#034;rhythms&#034;: [0.5, 0.5, 0.25, 0.25, 0.5, 1.0],<br \/>\n             &#034;step_pref&#034;: 3, &#034;oct_range&#034;: (-2, 3), &#034;chromatic_prob&#034;: 0.25},<br \/>\n    &#034;\u5e73\u9759&#034;: {&#034;scale&#034;: PENTATONIC_MAJOR, &#034;rhythms&#034;: [1.0, 1.0, 0.5, 0.5, 2.0],<br \/>\n             &#034;step_pref&#034;: 1, &#034;oct_range&#034;: (-2, 1), &#034;chromatic_prob&#034;: 0.05},<br \/>\n    &#034;\u53e4\u98ce&#034;: {&#034;scale&#034;: PENTATONIC_MAJOR, &#034;rhythms&#034;: [1.0, 0.5, 0.5, 1.0, 0.5, 0.5],<br \/>\n             &#034;step_pref&#034;: 1, &#034;oct_range&#034;: (-2, 1), &#034;chromatic_prob&#034;: 0.05},<br \/>\n    &#034;\u8fdb\u884c\u66f2&#034;: {&#034;scale&#034;: MAJOR_SCALE, &#034;rhythms&#034;: [0.5, 0.5, 1.0, 0.5, 0.5, 1.0],<br \/>\n               &#034;step_pref&#034;: 2, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.15},<br \/>\n    &#034;\u821e\u66f2&#034;: {&#034;scale&#034;: MAJOR_SCALE, &#034;rhythms&#034;: [0.25, 0.25, 0.5, 0.25, 0.25, 0.5, 1.0],<br \/>\n             &#034;step_pref&#034;: 2, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.2},<br \/>\n    &#034;\u6447\u6eda&#034;: {&#034;scale&#034;: MAJOR_SCALE, &#034;rhythms&#034;: [0.5, 0.25, 0.25, 0.5, 1.0, 0.5],<br \/>\n             &#034;step_pref&#034;: 3, &#034;oct_range&#034;: (-2, 3), &#034;chromatic_prob&#034;: 0.3},<br \/>\n    &#034;\u7235\u58eb&#034;: {&#034;scale&#034;: MAJOR_SCALE, &#034;rhythms&#034;: [0.5, 0.25, 0.5, 0.25, 0.5, 1.0],<br \/>\n             &#034;step_pref&#034;: 2, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.5},<br \/>\n    &#034;\u84dd\u8c03&#034;: {&#034;scale&#034;: BLUES_SCALE, &#034;rhythms&#034;: [0.5, 0.5, 1.0, 0.5, 0.5, 1.0],<br \/>\n             &#034;step_pref&#034;: 2, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.3},<br \/>\n    &#034;\u5168\u97f3\u9636&#034;: {&#034;scale&#034;: WHOLE_TONE, &#034;rhythms&#034;: [0.5, 1.0, 0.5, 1.0, 0.5],<br \/>\n               &#034;step_pref&#034;: 1, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.0},<br \/>\n    &#034;\u516b\u58f0\u97f3\u9636&#034;: {&#034;scale&#034;: OCTATONIC, &#034;rhythms&#034;: [0.25, 0.5, 0.25, 0.5, 1.0],<br \/>\n                 &#034;step_pref&#034;: 1, &#034;oct_range&#034;: (-2, 2), &#034;chromatic_prob&#034;: 0.0},<br \/>\n}<\/p>\n<p># &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061; \u4f5c\u66f2\u6280\u672f\u5f15\u64ce &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;<\/p>\n<p>class CounterpointEngine:<br \/>\n    &#034;&#034;&#034;\u5bf9\u4f4d\u6cd5\u5f15\u64ce \u2014 \u652f\u6301\u4e8c\u58f0\u90e8\u3001\u4e09\u58f0\u90e8\u3001\u5361\u519c\u3001\u8d4b\u683c&#034;&#034;&#034;<\/p>\n<p>    INTERVAL_NAMES &#061; {<br \/>\n        0: &#034;\u7eaf\u4e00\u5ea6&#034;, 1: &#034;\u5c0f\u4e8c\u5ea6&#034;, 2: &#034;\u5927\u4e8c\u5ea6&#034;, 3: &#034;\u5c0f\u4e09\u5ea6&#034;,<br \/>\n        4: &#034;\u5927\u4e09\u5ea6&#034;, 5: &#034;\u7eaf\u56db\u5ea6&#034;, 6: &#034;\u589e\u56db\u5ea6\/\u51cf\u4e94\u5ea6&#034;, 7: &#034;\u7eaf\u4e94\u5ea6&#034;,<br \/>\n        8: &#034;\u5c0f\u516d\u5ea6&#034;, 9: &#034;\u5927\u516d\u5ea6&#034;, 10: &#034;\u5c0f\u4e03\u5ea6&#034;, 11: &#034;\u5927\u4e03\u5ea6&#034;, 12: &#034;\u7eaf\u516b\u5ea6&#034;<br \/>\n    }<\/p>\n<p>    # \u534f\u548c\u5ea6&#xff1a;3&#061;\u5b8c\u5168\u534f\u548c, 2&#061;\u4e0d\u5b8c\u5168\u534f\u548c, 1&#061;\u4e0d\u534f\u548c, 0&#061;\u7981\u7528<br \/>\n    CONSONANCE &#061; {<br \/>\n        0: 3, 7: 3, 12: 3,<br \/>\n        3: 2, 4: 2, 8: 2, 9: 2,<br \/>\n        5: 2,<br \/>\n        2: 1, 10: 1,<br \/>\n        1: 1, 11: 1,<br \/>\n        6: 0,<br \/>\n    }<\/p>\n<p>    def __init__(self, key_offset&#061;0, scale&#061;None):<br \/>\n        self.key &#061; key_offset<br \/>\n        self.scale &#061; scale or MAJOR_SCALE<br \/>\n        self.voices &#061; []<br \/>\n        self.errors &#061; []<\/p>\n<p>    def get_interval(self, p1, p2):<br \/>\n        return abs(p1 &#8211; p2) % 12<\/p>\n<p>    def is_parallel_fifth(self, v1_prev, v1_curr, v2_prev, v2_curr):<br \/>\n        prev_int &#061; self.get_interval(v1_prev, v2_prev)<br \/>\n        curr_int &#061; self.get_interval(v1_curr, v2_curr)<br \/>\n        return prev_int &#061;&#061; 7 and curr_int &#061;&#061; 7 and v1_prev !&#061; v1_curr<\/p>\n<p>    def is_parallel_octave(self, v1_prev, v1_curr, v2_prev, v2_curr):<br \/>\n        prev_int &#061; self.get_interval(v1_prev, v2_prev)<br \/>\n        curr_int &#061; self.get_interval(v1_curr, v2_curr)<br \/>\n        return prev_int &#061;&#061; 0 and curr_int &#061;&#061; 0 and v1_prev !&#061; v1_curr<\/p>\n<p>    def generate_cantus_firmus(self, n_notes, start_note, scale&#061;None):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5b9a\u65cb\u5f8b&#xff08;Cantus Firmus&#xff09;&#034;&#034;&#034;<br \/>\n        scale &#061; scale or self.scale<br \/>\n        notes &#061; [start_note]<\/p>\n<p>        for i in range(1, n_notes):<br \/>\n            prev &#061; notes[-1]<br \/>\n            step_weights &#061; [0.5, 0.3, 0.15, 0.05]<br \/>\n            step_type &#061; random.choices([1, 3, 5, random.randint(2, 7)], weights&#061;step_weights)[0]<br \/>\n            direction &#061; random.choice([-1, 1])<br \/>\n            new_note &#061; prev &#043; direction * step_type<\/p>\n<p>            new_semi &#061; new_note % 12<br \/>\n            if new_semi not in scale:<br \/>\n                candidates &#061; [(s, abs(s &#8211; new_semi)) for s in scale]<br \/>\n                candidates.sort(key&#061;lambda x: x[1])<br \/>\n                new_note &#061; (new_note \/\/ 12) * 12 &#043; candidates[0][0]<\/p>\n<p>            new_note &#061; max(start_note &#8211; 12, min(start_note &#043; 12, new_note))<br \/>\n            notes.append(new_note)<\/p>\n<p>        final &#061; start_note<br \/>\n        if notes[-1] !&#061; final:<br \/>\n            notes[-1] &#061; final<br \/>\n        return notes<\/p>\n<p>    def generate_counterpoint(self, cantus, voice_type&#061;&#034;above&#034;, species&#061;1):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5bf9\u4f4d\u65cb\u5f8b&#034;&#034;&#034;<br \/>\n        cp &#061; []<br \/>\n        octave_offset &#061; 12 if voice_type &#061;&#061; &#034;above&#034; else -12<\/p>\n<p>        for i, cf_note in enumerate(cantus):<br \/>\n            target &#061; cf_note &#043; octave_offset<\/p>\n<p>            if species &#061;&#061; 1:<br \/>\n                candidates &#061; []<br \/>\n                for offset in [-3, -4, -5, 3, 4, 5, 7, 8, 12]:<br \/>\n                    cand &#061; target &#043; offset<br \/>\n                    if cand % 12 in self.scale:<br \/>\n                        candidates.append(cand)<\/p>\n<p>                if candidates:<br \/>\n                    cp.append(random.choice(candidates))<br \/>\n                else:<br \/>\n                    cp.append(target &#043; random.choice([3, 4, 5]))<\/p>\n<p>            elif species &#061;&#061; 2:<br \/>\n                if i &lt; len(cantus) &#8211; 1:<br \/>\n                    first &#061; target &#043; random.choice([3, 4, 5, 7, 8])<br \/>\n                    step &#061; random.choice([-1, 1])<br \/>\n                    second &#061; first &#043; step<br \/>\n                    cp.extend([first, second])<br \/>\n                else:<br \/>\n                    cp.append(target &#043; 3)<\/p>\n<p>        return cp<\/p>\n<p>    def generate_canon(self, theme, interval&#061;4, delay&#061;2):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u5361\u519c&#034;&#034;&#034;<br \/>\n        voices &#061; [theme]<br \/>\n        imitation &#061; [(n &#043; interval) % 12 &#043; (n \/\/ 12) * 12 for n in theme]<br \/>\n        voices.append(imitation)<br \/>\n        return voices<\/p>\n<p>    def generate_fugue_subject(self, scale, length&#061;8):<br \/>\n        &#034;&#034;&#034;\u751f\u6210\u8d4b\u683c\u4e3b\u9898&#034;&#034;&#034;<br \/>\n        subject &#061; []<br \/>\n        tonic &#061; scale[0]<br \/>\n        dominant &#061; scale[4] if len(scale) &gt;&#061; 5 else scale[0] &#043; 7<\/p>\n<p>        subject.append(tonic)<br \/>\n        subject.append(dominant)<\/p>\n<p>        direction &#061; -1 if random.random() &lt; 0.5 else 1<br \/>\n        current &#061; dominant<br \/>\n        for i in range(length &#8211; 3):<br \/>\n            step &#061; random.choice([1, 2]) * direction<br \/>\n            current &#043;&#061; step<br \/>\n            current_semi &#061; current % 12<br \/>\n            if current_semi not in scale:<br \/>\n                current &#061; (current \/\/ 12) * 12 &#043; min(scale, key&#061;lambda s: abs(s &#8211; current_semi))<br \/>\n            subject.append(current)<\/p>\n<p>        subject.append(tonic)<br \/>\n        return subject<\/p>\n<p>class HarmonyEngine:<br \/>\n    &#034;&#034;&#034;\u548c\u58f0\u5b66\u5f15\u64ce \u2014 \u56db\u90e8\u548c\u58f0\u3001\u548c\u5f26\u8fdb\u884c\u3001\u8c03\u5f0f\u4ea4\u66ff&#034;&#034;&#034;<\/p>\n<p>    CHORD_TYPES &#061; {<br \/>\n        &#034;I&#034;:   [0, 4, 7],<br \/>\n        &#034;ii&#034;:  [2, 5, 9],<br \/>\n        &#034;iii&#034;: [4, 7, 11],<br \/>\n        &#034;IV&#034;:  [5, 9, 0],<br \/>\n        &#034;V&#034;:   [7, 11, 2],<br \/>\n        &#034;vi&#034;:  [9, 0, 4],<br \/>\n        &#034;vii\u00b0&#034;: [11, 2, 5],<br \/>\n        &#034;V7&#034;:  [7, 11, 2, 5],<br \/>\n        &#034;ii7&#034;: [2, 5, 9, 0],<br \/>\n        &#034;viio7&#034;: [11, 2, 5, 8],<br \/>\n        &#034;V\/V&#034;: [2, 6, 9],<br \/>\n        &#034;V\/ii&#034;: [9, 1, 4],<br \/>\n    }<\/p>\n<p>    PROGRESSIONS &#061; {<br \/>\n        &#034;\u53e4\u5178\u7ec8\u6b62&#034;: [&#034;I&#034;, &#034;IV&#034;, &#034;V&#034;, &#034;I&#034;],<br \/>\n        &#034;\u6b63\u683c\u8fdb\u884c&#034;: [&#034;I&#034;, &#034;V&#034;, &#034;I&#034;],<br \/>\n        &#034;\u53d8\u683c\u8fdb\u884c&#034;: [&#034;I&#034;, &#034;IV&#034;, &#034;I&#034;],<br \/>\n        &#034;\u534a\u7ec8\u6b62&#034;: [&#034;I&#034;, &#034;IV&#034;, &#034;V&#034;],<br \/>\n        &#034;\u963b\u788d\u7ec8\u6b62&#034;: [&#034;I&#034;, &#034;V&#034;, &#034;vi&#034;],<br \/>\n        &#034;\u5b8c\u5168\u8fdb\u884c&#034;: [&#034;I&#034;, &#034;vi&#034;, &#034;ii&#034;, &#034;V&#034;, &#034;I&#034;],<br \/>\n        &#034;\u7235\u58ebii-V-I&#034;: [&#034;ii7&#034;, &#034;V7&#034;, &#034;I&#034;],<br \/>\n        &#034;\u6d41\u884c\u8fdb\u884c&#034;: [&#034;I&#034;, &#034;V&#034;, &#034;vi&#034;, &#034;IV&#034;],<br \/>\n        &#034;\u84dd\u8c03\u8fdb\u884c&#034;: [&#034;I&#034;, &#034;IV&#034;, &#034;I&#034;, &#034;V&#034;, &#034;IV&#034;, &#034;I&#034;],<br \/>\n        &#034;\u8c03\u5f0f\u4ea4\u66ff&#034;: [&#034;I&#034;, &#034;ii&#034;, &#034;iii&#034;, &#034;IV&#034;, &#034;V&#034;, &#034;vi&#034;, &#034;vii\u00b0&#034;, &#034;I&#034;],<br \/>\n    }<\/p>\n<p>    def __init__(self, key_offset&#061;0, scale&#061;None):<br \/>\n        self.key &#061; key_offset<br \/>\n        self.scale &#061; scale or MAJOR_SCALE<br \/>\n        self.chords &#061; []<\/p>\n<p>    def get_chord_pitches(self, chord_name, inversion&#061;0):<br \/>\n        base_pitches &#061; self.CHORD_TYPES.get(chord_name, [0, 4, 7])<br \/>\n        if inversion &gt; 0:<br \/>\n            base_pitches &#061; base_pitches[inversion:] &#043; [p &#043; 12 for p in base_pitches[:inversion]]<br \/>\n        return [(p &#043; self.key) % 12 for p in base_pitches]<\/p>\n<p>    def generate_four_part(self, progression_name, n_bars&#061;4):<br \/>\n        progression &#061; self.PROGRESSIONS.get(progression_name, self.PROGRESSIONS[&#034;\u53e4\u5178\u7ec8\u6b62&#034;])<br \/>\n        ranges &#061; {&#039;S&#039;: (60, 81), &#039;A&#039;: (55, 74), &#039;T&#039;: (48, 67), &#039;B&#039;: (40, 60)}<br \/>\n        voices &#061; {&#039;S&#039;: [], &#039;A&#039;: [], &#039;T&#039;: [], &#039;B&#039;: []}<\/p>\n<p>        for bar in range(n_bars):<br \/>\n            chord_name &#061; progression[bar % len(progression)]<br \/>\n            chord_pitches &#061; self.get_chord_pitches(chord_name)<\/p>\n<p>            for voice_name in [&#039;B&#039;, &#039;T&#039;, &#039;A&#039;, &#039;S&#039;]:<br \/>\n                v_range &#061; ranges[voice_name]<br \/>\n                candidates &#061; []<br \/>\n                for p in chord_pitches:<br \/>\n                    for octv in range(3, 7):<br \/>\n                        midi &#061; p &#043; octv * 12<br \/>\n                        if v_range[0] &lt;&#061; midi &lt;&#061; v_range[1]:<br \/>\n                            candidates.append(midi)<\/p>\n<p>                if candidates:<br \/>\n                    if voices[voice_name]:<br \/>\n                        prev &#061; voices[voice_name][-1]<br \/>\n                        candidates.sort(key&#061;lambda x: abs(x &#8211; prev))<br \/>\n                        chosen &#061; candidates[0]<br \/>\n                    else:<br \/>\n                        chosen &#061; random.choice(candidates)<br \/>\n                    voices[voice_name].append(chosen)<br \/>\n                else:<br \/>\n                    voices[voice_name].append(v_range[0] &#043; 4)<\/p>\n<p>        return voices<\/p>\n<p>    def generate_jazz_changes(self, n_bars&#061;8):<br \/>\n        jazz_progressions &#061; [<br \/>\n            [&#034;ii7&#034;, &#034;V7&#034;, &#034;I&#034;, &#034;I&#034;],<br \/>\n            [&#034;I&#034;, &#034;vi&#034;, &#034;ii7&#034;, &#034;V7&#034;],<br \/>\n            [&#034;ii7&#034;, &#034;V7&#034;, &#034;iii&#034;, &#034;vi&#034;],<br \/>\n            [&#034;I&#034;, &#034;IV&#034;, &#034;ii7&#034;, &#034;V7&#034;],<br \/>\n        ]<br \/>\n        result &#061; []<br \/>\n        for bar in range(n_bars):<br \/>\n            prog &#061; jazz_progressions[bar % len(jazz_progressions)]<br \/>\n            chord &#061; prog[bar % len(prog)]<br \/>\n            result.append(chord)<br \/>\n        return result<\/p>\n<p>class FormEngine:<br \/>\n    &#034;&#034;&#034;\u66f2\u5f0f\u5b66\u5f15\u64ce&#034;&#034;&#034;<\/p>\n<p>    def __init__(self, melody_engine&#061;None):<br \/>\n        self.melody_engine &#061; melody_engine<\/p>\n<p>    def generate_binary(self, theme, n_bars_a&#061;8, n_bars_b&#061;8, style&#061;None):<br \/>\n        style &#061; style or STYLES[&#034;\u6292\u60c5&#034;]<br \/>\n        section_a &#061; theme[:n_bars_a] if len(theme) &gt;&#061; n_bars_a else theme<\/p>\n<p>        dominant_shift &#061; 7<br \/>\n        section_b &#061; []<br \/>\n        for semi, octv, dur in theme[:n_bars_b]:<br \/>\n            new_semi &#061; (semi &#043; dominant_shift) % 12<br \/>\n            section_b.append((new_semi, octv, dur))<\/p>\n<p>        return {&#034;A&#034;: section_a, &#034;B&#034;: section_b}<\/p>\n<p>    def generate_ternary(self, theme, n_bars&#061;8, style&#061;None):<br \/>\n        style &#061; style or STYLES[&#034;\u6292\u60c5&#034;]<br \/>\n        section_a &#061; theme[:n_bars] if len(theme) &gt;&#061; n_bars else theme<\/p>\n<p>        section_b &#061; []<br \/>\n        for semi, octv, dur in section_a:<br \/>\n            new_oct &#061; octv &#043; random.choice([-1, 1])<br \/>\n            new_dur &#061; dur \/ 2 if dur &gt;&#061; 0.5 else dur<br \/>\n            section_b.append((semi, new_oct, new_dur))<\/p>\n<p>        section_a_prime &#061; []<br \/>\n        for semi, octv, dur in section_a:<br \/>\n            if random.random() &lt; 0.3:<br \/>\n                neighbor &#061; semi &#043; random.choice([-1, 1])<br \/>\n                section_a_prime.append((neighbor, octv, dur * 0.25))<br \/>\n                section_a_prime.append((semi, octv, dur * 0.75))<br \/>\n            else:<br \/>\n                section_a_prime.append((semi, octv, dur))<\/p>\n<p>        return {&#034;A&#034;: section_a, &#034;B&#034;: section_b, &#034;A&#039;&#034;: section_a_prime}<\/p>\n<p>    def generate_rondo(self, theme, episodes&#061;2, style&#061;None):<br \/>\n        style &#061; style or STYLES[&#034;\u6b22\u5feb&#034;]<br \/>\n        result &#061; {&#034;A&#034;: theme}<\/p>\n<p>        for i in range(episodes):<br \/>\n            episode &#061; []<br \/>\n            for semi, octv, dur in theme:<br \/>\n                new_semi &#061; (semi &#043; 2) % 12<br \/>\n                new_oct &#061; octv &#043; random.choice([-1, 0, 1])<br \/>\n                episode.append((new_semi, new_oct, dur))<br \/>\n            result[f&#034;\u63d2\u90e8{i&#043;1}&#034;] &#061; episode<\/p>\n<p>        return result<\/p>\n<p>    def generate_variations(self, theme, n_variations&#061;3, style&#061;None):<br \/>\n        style &#061; style or STYLES[&#034;\u6292\u60c5&#034;]<br \/>\n        variations &#061; {&#034;\u4e3b\u9898&#034;: theme}<\/p>\n<p>        techniques &#061; [&#034;\u88c5\u9970\u53d8\u594f&#034;, &#034;\u8282\u594f\u53d8\u594f&#034;, &#034;\u8c03\u5f0f\u53d8\u594f&#034;, &#034;\u97f3\u533a\u53d8\u594f&#034;]<\/p>\n<p>        for i in range(n_variations):<br \/>\n            tech &#061; techniques[i % len(techniques)]<br \/>\n            var &#061; []<\/p>\n<p>            for semi, octv, dur in theme:<br \/>\n                if tech &#061;&#061; &#034;\u88c5\u9970\u53d8\u594f&#034;:<br \/>\n                    if random.random() &lt; 0.4:<br \/>\n                        passing &#061; semi &#043; random.choice([-1, 1])<br \/>\n                        var.append((passing, octv, dur * 0.3))<br \/>\n                        var.append((semi, octv, dur * 0.7))<br \/>\n                    else:<br \/>\n                        var.append((semi, octv, dur))<\/p>\n<p>                elif tech &#061;&#061; &#034;\u8282\u594f\u53d8\u594f&#034;:<br \/>\n                    new_dur &#061; dur * random.choice([0.5, 1.0, 2.0])<br \/>\n                    var.append((semi, octv, new_dur))<\/p>\n<p>                elif tech &#061;&#061; &#034;\u8c03\u5f0f\u53d8\u594f&#034;:<br \/>\n                    new_semi &#061; semi<br \/>\n                    if semi in [4, 9, 11]:<br \/>\n                        new_semi &#061; semi &#8211; 1<br \/>\n                    var.append((new_semi, octv, dur))<\/p>\n<p>                elif tech &#061;&#061; &#034;\u97f3\u533a\u53d8\u594f&#034;:<br \/>\n                    new_oct &#061; octv &#043; random.choice([-1, 1])<br \/>\n                    var.append((semi, new_oct, dur))<br \/>\n                else:<br \/>\n                    var.append((semi, octv, dur))<\/p>\n<p>            variations[f&#034;\u53d8\u594f{i&#043;1}({tech})&#034;] &#061; var<\/p>\n<p>        return variations<\/p>\n<p>    def generate_sonata(self, theme_group1, theme_group2, n_bars&#061;16, style&#061;None):<br \/>\n        style &#061; style or STYLES[&#034;\u6fc0\u6602&#034;]<\/p>\n<p>        exposition &#061; {<br \/>\n            &#034;\u4e3b\u90e8(\u4e3b\u8c03)&#034;: theme_group1[:n_bars\/\/2],<br \/>\n            &#034;\u8fde\u63a5\u90e8&#034;: theme_group1[n_bars\/\/2:n_bars\/\/2&#043;4],<br \/>\n            &#034;\u526f\u90e8(\u5c5e\u8c03)&#034;: [(semi&#043;7, octv, dur) for semi, octv, dur in theme_group2[:n_bars\/\/2]],<br \/>\n        }<\/p>\n<p>        development &#061; []<br \/>\n        for semi, octv, dur in theme_group1 &#043; theme_group2:<br \/>\n            new_semi &#061; (semi &#043; random.randint(1, 6)) % 12<br \/>\n            new_dur &#061; dur * random.choice([0.5, 0.5, 0.25])<br \/>\n            development.append((new_semi, octv, new_dur))<\/p>\n<p>        recapitulation &#061; {<br \/>\n            &#034;\u4e3b\u90e8\u518d\u73b0&#034;: theme_group1[:n_bars\/\/2],<br \/>\n            &#034;\u526f\u90e8\u518d\u73b0(\u4e3b\u8c03)&#034;: theme_group2[:n_bars\/\/2],<br \/>\n            &#034;\u5c3e\u58f0&#034;: theme_group1[:4],<br \/>\n        }<\/p>\n<p>        return {&#034;\u5448\u793a\u90e8&#034;: exposition, &#034;\u5c55\u5f00\u90e8&#034;: development, &#034;\u518d\u73b0\u90e8&#034;: recapitulation}<\/p>\n<p>class OrchestrationEngine:<br \/>\n    &#034;&#034;&#034;\u914d\u5668\u6cd5\u5f15\u64ce&#034;&#034;&#034;<\/p>\n<p>    INSTRUMENTS &#061; {<br \/>\n        &#034;\u94a2\u7434&#034;: 0, &#034;\u660e\u4eae\u94a2\u7434&#034;: 1, &#034;\u7535\u94a2\u7434&#034;: 2, &#034;\u9152\u5427\u94a2\u7434&#034;: 3,<br \/>\n        &#034;\u98a4\u97f3\u7434&#034;: 11, &#034;\u9a6c\u6797\u5df4&#034;: 12, &#034;\u6728\u7434&#034;: 13, &#034;\u7ba1\u949f&#034;: 14,<br \/>\n        &#034;\u98ce\u7434&#034;: 16, &#034;\u6559\u5802\u98ce\u7434&#034;: 19, &#034;\u624b\u98ce\u7434&#034;: 21, &#034;\u53e3\u7434&#034;: 22,<br \/>\n        &#034;\u5c3c\u9f99\u5409\u4ed6&#034;: 24, &#034;\u94a2\u5f26\u5409\u4ed6&#034;: 25, &#034;\u7535\u5409\u4ed6&#034;: 27, &#034;\u5931\u771f\u5409\u4ed6&#034;: 29,<br \/>\n        &#034;\u8d1d\u53f8&#034;: 32, &#034;\u7535\u8d1d\u53f8&#034;: 33, &#034;\u65e0\u54c1\u8d1d\u53f8&#034;: 35,<br \/>\n        &#034;\u5c0f\u63d0\u7434&#034;: 40, &#034;\u4e2d\u63d0\u7434&#034;: 41, &#034;\u5927\u63d0\u7434&#034;: 42, &#034;\u4f4e\u97f3\u63d0\u7434&#034;: 43,<br \/>\n        &#034;\u5f26\u4e50\u5408\u594f&#034;: 48, &#034;\u5f26\u4e50\u62e8\u594f&#034;: 45, &#034;\u7ad6\u7434&#034;: 46,<br \/>\n        &#034;\u957f\u7b1b&#034;: 73, &#034;\u53cc\u7c27\u7ba1&#034;: 68, &#034;\u5355\u7c27\u7ba1&#034;: 71, &#034;\u5df4\u677e&#034;: 70,<br \/>\n        &#034;\u5706\u53f7&#034;: 60, &#034;\u5c0f\u53f7&#034;: 56, &#034;\u957f\u53f7&#034;: 57, &#034;\u5927\u53f7&#034;: 58,<br \/>\n        &#034;\u94dc\u7ba1\u5408\u594f&#034;: 61, &#034;\u5408\u6210\u5668\u4e3b\u97f3&#034;: 80, &#034;\u5408\u6210\u5668\u94fa\u5e95&#034;: 88,<br \/>\n        &#034;\u7ba1\u5f26\u4e50\u6253\u51fb&#034;: 47, &#034;\u5b9a\u97f3\u9f13&#034;: 47,<br \/>\n    }<\/p>\n<p>    RANGES &#061; {<br \/>\n        &#034;\u94a2\u7434&#034;: (21, 108), &#034;\u5c0f\u63d0\u7434&#034;: (55, 103), &#034;\u4e2d\u63d0\u7434&#034;: (48, 91),<br \/>\n        &#034;\u5927\u63d0\u7434&#034;: (36, 76), &#034;\u4f4e\u97f3\u63d0\u7434&#034;: (28, 67),<br \/>\n        &#034;\u957f\u7b1b&#034;: (60, 96), &#034;\u53cc\u7c27\u7ba1&#034;: (58, 91), &#034;\u5355\u7c27\u7ba1&#034;: (50, 94),<br \/>\n        &#034;\u5df4\u677e&#034;: (34, 75), &#034;\u5706\u53f7&#034;: (31, 77), &#034;\u5c0f\u53f7&#034;: (54, 86),<br \/>\n        &#034;\u957f\u53f7&#034;: (40, 72), &#034;\u5927\u53f7&#034;: (28, 58),<br \/>\n        &#034;\u4eba\u58f0-\u5973\u9ad8&#034;: (60, 84), &#034;\u4eba\u58f0-\u5973\u4f4e&#034;: (53, 77),<br \/>\n        &#034;\u4eba\u58f0-\u7537\u9ad8&#034;: (48, 72), &#034;\u4eba\u58f0-\u7537\u4f4e&#034;: (40, 64),<br \/>\n    }<\/p>\n<p>    ENSEMBLES &#061; {<br \/>\n        &#034;\u5f26\u4e50\u56db\u91cd\u594f&#034;: [&#034;\u5c0f\u63d0\u7434&#034;, &#034;\u5c0f\u63d0\u7434&#034;, &#034;\u4e2d\u63d0\u7434&#034;, &#034;\u5927\u63d0\u7434&#034;],<br \/>\n        &#034;\u94a2\u7434\u4e09\u91cd\u594f&#034;: [&#034;\u94a2\u7434&#034;, &#034;\u5c0f\u63d0\u7434&#034;, &#034;\u5927\u63d0\u7434&#034;],<br \/>\n        &#034;\u94dc\u7ba1\u4e94\u91cd\u594f&#034;: [&#034;\u5c0f\u53f7&#034;, &#034;\u5c0f\u53f7&#034;, &#034;\u5706\u53f7&#034;, &#034;\u957f\u53f7&#034;, &#034;\u5927\u53f7&#034;],<br \/>\n        &#034;\u6728\u7ba1\u4e94\u91cd\u594f&#034;: [&#034;\u957f\u7b1b&#034;, &#034;\u53cc\u7c27\u7ba1&#034;, &#034;\u5355\u7c27\u7ba1&#034;, &#034;\u5df4\u677e&#034;, &#034;\u5706\u53f7&#034;],<br \/>\n        &#034;\u7ba1\u5f26\u4e50\u961f&#034;: [&#034;\u5c0f\u63d0\u7434&#034;, &#034;\u4e2d\u63d0\u7434&#034;, &#034;\u5927\u63d0\u7434&#034;, &#034;\u4f4e\u97f3\u63d0\u7434&#034;,<br \/>\n                     &#034;\u957f\u7b1b&#034;, &#034;\u53cc\u7c27\u7ba1&#034;, &#034;\u5355\u7c27\u7ba1&#034;, &#034;\u5df4\u677e&#034;,<br \/>\n                     &#034;\u5706\u53f7&#034;, &#034;\u5c0f\u53f7&#034;, &#034;\u957f\u53f7&#034;, &#034;\u5927\u53f7&#034;, &#034;\u94a2\u7434&#034;],<br \/>\n        &#034;\u6d41\u884c\u4e50\u961f&#034;: [&#034;\u94a2\u7434&#034;, &#034;\u7535\u5409\u4ed6&#034;, &#034;\u8d1d\u53f8&#034;, &#034;\u5408\u6210\u5668\u4e3b\u97f3&#034;, &#034;\u5f26\u4e50\u5408\u594f&#034;],<br \/>\n    }<\/p>\n<p>    def __init__(self):<br \/>\n        self.assignments &#061; []<\/p>\n<p>    def assign_instruments(self, melody, ensemble_name&#061;&#034;\u5f26\u4e50\u56db\u91cd\u594f&#034;):<br \/>\n        instruments &#061; self.ENSEMBLES.get(ensemble_name, [&#034;\u94a2\u7434&#034;])<br \/>\n        tracks &#061; []<\/p>\n<p>        for i, inst_name in enumerate(instruments):<br \/>\n            inst_id &#061; self.INSTRUMENTS.get(inst_name, 0)<br \/>\n            inst_range &#061; self.RANGES.get(inst_name, (36, 84))<\/p>\n<p>            track &#061; []<br \/>\n            for semi, octv, dur in melody:<br \/>\n                midi &#061; (4 &#043; octv) * 12 &#043; semi<\/p>\n<p>                if midi &lt; inst_range[0]:<br \/>\n                    midi &#043;&#061; 12<br \/>\n                elif midi &gt; inst_range[1]:<br \/>\n                    midi -&#061; 12<\/p>\n<p>                oct_adjust &#061; 0<br \/>\n                if inst_name in [&#034;\u4f4e\u97f3\u63d0\u7434&#034;, &#034;\u5927\u53f7&#034;, &#034;\u4eba\u58f0-\u7537\u4f4e&#034;]:<br \/>\n                    oct_adjust &#061; -1<br \/>\n                elif inst_name in [&#034;\u5c0f\u63d0\u7434&#034;, &#034;\u957f\u7b1b&#034;, &#034;\u5c0f\u53f7&#034;]:<br \/>\n                    oct_adjust &#061; 1<\/p>\n<p>                new_semi &#061; midi % 12<br \/>\n                new_oct &#061; (midi \/\/ 12) &#8211; 4 &#043; oct_adjust<br \/>\n                track.append((new_semi, new_oct, dur, inst_id, inst_name))<\/p>\n<p>            tracks.append(track)<\/p>\n<p>        return tracks<\/p>\n<p>    def generate_texture(self, melody, texture_type&#061;&#034;\u4e3b\u8c03&#034;):<br \/>\n        if texture_type &#061;&#061; &#034;\u4e3b\u8c03&#034;:<br \/>\n            accompaniment &#061; []<br \/>\n            for semi, octv, dur in melody:<br \/>\n                third &#061; (semi &#043; 4) % 12<br \/>\n                fifth &#061; (semi &#043; 7) % 12<br \/>\n                accompaniment.append([<br \/>\n                    (semi, octv &#8211; 1, dur, &#034;\u65cb\u5f8b&#034;),<br \/>\n                    (third, octv &#8211; 1, dur, &#034;\u4e09\u97f3&#034;),<br \/>\n                    (fifth, octv &#8211; 1, dur, &#034;\u4e94\u97f3&#034;),<br \/>\n                ])<br \/>\n            return {&#034;\u65cb\u5f8b&#034;: melody, &#034;\u4f34\u594f&#034;: accompaniment}<\/p>\n<p>        elif texture_type &#061;&#061; &#034;\u590d\u8c03&#034;:<br \/>\n            cp &#061; CounterpointEngine()<br \/>\n            cantus &#061; [semi &#043; (4 &#043; octv) * 12 for semi, octv, dur in melody]<br \/>\n            counter &#061; cp.generate_counterpoint(cantus, &#034;below&#034;, 1)<br \/>\n            counter_melody &#061; [(c % 12, c \/\/ 12 &#8211; 4, 1.0) for c in counter]<br \/>\n            return {&#034;\u9ad8\u97f3&#034;: melody, &#034;\u4f4e\u97f3&#034;: counter_melody}<\/p>\n<p>        elif texture_type &#061;&#061; &#034;\u5206\u89e3\u548c\u5f26&#034;:<br \/>\n            arpeggio &#061; []<br \/>\n            for semi, octv, dur in melody:<br \/>\n                chord_tones &#061; [semi, (semi &#043; 4) % 12, (semi &#043; 7) % 12]<br \/>\n                for i, tone in enumerate(chord_tones):<br \/>\n                    arpeggio.append((tone, octv &#8211; 1, dur \/ 3))<br \/>\n            return {&#034;\u65cb\u5f8b&#034;: melody, &#034;\u7436\u97f3&#034;: arpeggio}<\/p>\n<p>        elif texture_type &#061;&#061; &#034;\u6301\u7eed\u97f3&#034;:<br \/>\n            drone &#061; []<br \/>\n            tonic &#061; melody[0][0] if melody else 0<br \/>\n            for _ in range(len(melody)):<br \/>\n                drone.append((tonic, -2, 4.0))<br \/>\n            return {&#034;\u65cb\u5f8b&#034;: melody, &#034;\u6301\u7eed\u97f3&#034;: drone}<\/p>\n<p>        return {&#034;\u65cb\u5f8b&#034;: melody}<\/p>\n<p>class TwelveToneEngine:<br \/>\n    &#034;&#034;&#034;\u5341\u4e8c\u97f3\u6280\u6cd5\u5f15\u64ce&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.row &#061; None<br \/>\n        self.matrix &#061; None<\/p>\n<p>    def generate_row(self):<br \/>\n        row &#061; list(range(12))<br \/>\n        random.shuffle(row)<br \/>\n        self.row &#061; row<br \/>\n        return row<\/p>\n<p>    def build_matrix(self):<br \/>\n        if not self.row:<br \/>\n            self.generate_row()<\/p>\n<p>        matrix &#061; []<br \/>\n        for i in range(12):<br \/>\n            transposed &#061; [(x &#043; i) % 12 for x in self.row]<br \/>\n            matrix.append(transposed)<\/p>\n<p>        self.matrix &#061; matrix<br \/>\n        return matrix<\/p>\n<p>    def get_transformations(self):<br \/>\n        if not self.row:<br \/>\n            self.generate_row()<\/p>\n<p>        P &#061; self.row<br \/>\n        I &#061; [(-x) % 12 for x in self.row]<br \/>\n        R &#061; list(reversed(self.row))<br \/>\n        RI &#061; [(-x) % 12 for x in reversed(self.row)]<\/p>\n<p>        return {&#034;P&#034;: P, &#034;I&#034;: I, &#034;R&#034;: R, &#034;RI&#034;: RI}<\/p>\n<p>    def generate_piece(self, n_notes&#061;48, use_rhythm&#061;True):<br \/>\n        transforms &#061; self.get_transformations()<br \/>\n        forms &#061; [&#034;P&#034;, &#034;I&#034;, &#034;R&#034;, &#034;RI&#034;]<br \/>\n        piece &#061; []<\/p>\n<p>        for i in range(n_notes \/\/ 12):<br \/>\n            form &#061; forms[i % 4]<br \/>\n            row &#061; transforms[form]<br \/>\n            transposition &#061; random.randint(0, 11)<br \/>\n            transposed &#061; [(x &#043; transposition) % 12 for x in row]<\/p>\n<p>            for semi in transposed:<br \/>\n                dur &#061; random.choice([0.5, 1.0, 0.25]) if use_rhythm else 1.0<br \/>\n                octv &#061; random.randint(-1, 2)<br \/>\n                piece.append((semi, octv, dur))<\/p>\n<p>        return piece<\/p>\n<p>class RhythmEngine:<br \/>\n    &#034;&#034;&#034;\u8282\u594f\u5f15\u64ce&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.patterns &#061; []<\/p>\n<p>    def generate_polyrhythm(self, base_pulse, against_pulse, n_beats&#061;4):<br \/>\n        base_notes &#061; []<br \/>\n        against_notes &#061; []<\/p>\n<p>        for i in range(base_pulse):<br \/>\n            base_notes.append((i * n_beats \/ base_pulse, n_beats \/ base_pulse))<\/p>\n<p>        for i in range(against_pulse):<br \/>\n            against_notes.append((i * n_beats \/ against_pulse, n_beats \/ against_pulse))<\/p>\n<p>        return {&#034;\u57fa\u7840&#034;: base_notes, &#034;\u5bf9\u4f4d&#034;: against_notes}<\/p>\n<p>    def generate_syncopation(self, pattern, intensity&#061;0.5):<br \/>\n        syncopated &#061; []<br \/>\n        for dur in pattern:<br \/>\n            if random.random() &lt; intensity and dur &gt;&#061; 0.5:<br \/>\n                syncopated.append(dur * 0.6)<br \/>\n                syncopated.append(dur * 0.4)<br \/>\n            else:<br \/>\n                syncopated.append(dur)<br \/>\n        return syncopated<\/p>\n<p>    def develop_rhythm(self, motif, technique&#061;&#034;\u91cd\u590d&#034;):<br \/>\n        if technique &#061;&#061; &#034;\u91cd\u590d&#034;:<br \/>\n            return motif * 2<br \/>\n        elif technique &#061;&#061; &#034;\u589e\u503c&#034;:<br \/>\n            return [d * 2 for d in motif]<br \/>\n        elif technique &#061;&#061; &#034;\u51cf\u503c&#034;:<br \/>\n            return [d \/ 2 for d in motif]<br \/>\n        elif technique &#061;&#061; &#034;\u9006\u884c&#034;:<br \/>\n            return list(reversed(motif))<br \/>\n        elif technique &#061;&#061; &#034;\u4ea4\u9519&#034;:<br \/>\n            return motif &#043; [max(motif) &#8211; d &#043; min(motif) for d in motif]<br \/>\n        elif technique &#061;&#061; &#034;\u5206\u88c2&#034;:<br \/>\n            result &#061; []<br \/>\n            for d in motif:<br \/>\n                result.extend([d\/2, d\/2])<br \/>\n            return result<br \/>\n        return motif<\/p>\n<p>    def generate_groove(self, style_name&#061;&#034;funk&#034;, n_bars&#061;4):<br \/>\n        grooves &#061; {<br \/>\n            &#034;funk&#034;: [0.25, 0.25, 0.25, 0.25, 0.5, 0.5, 0.25, 0.25, 0.5],<br \/>\n            &#034;bossa&#034;: [0.5, 0.25, 0.25, 0.5, 0.25, 0.25, 0.5, 0.5],<br \/>\n            &#034;shuffle&#034;: [0.66, 0.33, 0.66, 0.33, 0.66, 0.33, 0.66, 0.33],<br \/>\n            &#034;latin&#034;: [0.25, 0.25, 0.5, 0.25, 0.25, 0.5, 0.25, 0.25, 0.25, 0.25],<br \/>\n            &#034;disco&#034;: [0.5, 0.5, 0.5, 0.5, 0.25, 0.25, 0.5, 0.5],<br \/>\n        }<br \/>\n        pattern &#061; grooves.get(style_name, [0.5, 0.5, 0.5, 0.5])<br \/>\n        return pattern * n_bars<\/p>\n<p>class ChanceMusicEngine:<br \/>\n    &#034;&#034;&#034;\u5076\u7136\u97f3\u4e50\u5f15\u64ce&#034;&#034;&#034;<\/p>\n<p>    def __init__(self):<br \/>\n        self.parameters &#061; {}<\/p>\n<p>    def dice_composition(self, n_bars&#061;8):<br \/>\n        fragments &#061; {<br \/>\n            1: [(0, 0, 1.0), (2, 0, 1.0), (4, 0, 1.0), (5, 0, 1.0)],<br \/>\n            2: [(0, 0, 0.5), (2, 0, 0.5), (4, 0, 1.0), (7, 0, 1.0), (5, 0, 1.0)],<br \/>\n            3: [(4, 0, 1.0), (2, 0, 1.0), (0, 0, 2.0)],<br \/>\n            4: [(0, 0, 0.5), (4, 0, 0.5), (7, 0, 0.5), (4, 0, 0.5), (5, 0, 1.0), (2, 0, 1.0)],<br \/>\n            5: [(2, 0, 1.0), (4, 0, 0.5), (5, 0, 0.5), (7, 0, 1.0), (5, 0, 0.5), (4, 0, 0.5)],<br \/>\n            6: [(7, 0, 0.5), (5, 0, 0.5), (4, 0, 1.0), (2, 0, 0.5), (0, 0, 0.5), (2, 0, 0.5), (4, 0, 0.5)],<br \/>\n        }<\/p>\n<p>        result &#061; []<br \/>\n        for bar in range(n_bars):<br \/>\n            dice &#061; random.randint(1, 6)<br \/>\n            fragment &#061; fragments[dice]<br \/>\n            result.extend(fragment)<\/p>\n<p>        return result<\/p>\n<p>    def aleatoric_piece(self, n_events&#061;50, pitch_range&#061;(48, 84)):<br \/>\n        piece &#061; []<br \/>\n        for _ in range(n_events):<br \/>\n            pitch &#061; random.randint(*pitch_range)<br \/>\n            dur &#061; random.choice([0.25, 0.5, 1.0, 2.0, 0.125])<br \/>\n            velocity &#061; random.randint(30, 120)<br \/>\n            piece.append((pitch % 12, pitch \/\/ 12 &#8211; 4, dur, velocity))<br \/>\n        return piece<\/p>\n<p>    def cellular_automata_music(self, rule&#061;30, n_generations&#061;32):<br \/>\n        cells &#061; [0] * n_generations<br \/>\n        cells[n_generations \/\/ 2] &#061; 1<\/p>\n<p>        piece &#061; []<br \/>\n        for gen in range(n_generations):<br \/>\n            active_cells &#061; [i for i, c in enumerate(cells) if c &#061;&#061; 1]<br \/>\n            for ac in active_cells:<br \/>\n                semi &#061; ac % 12<br \/>\n                octv &#061; ac \/\/ 12<br \/>\n                piece.append((semi, octv, 0.5))<\/p>\n<p>            new_cells &#061; cells[:]<br \/>\n            for i in range(1, n_generations &#8211; 1):<br \/>\n                pattern &#061; (cells[i-1], cells[i], cells[i&#043;1])<br \/>\n                new_cells[i] &#061; self._apply_rule(rule, pattern)<br \/>\n            cells &#061; new_cells<\/p>\n<p>        return piece<\/p>\n<p>    def _apply_rule(self, rule, pattern):<br \/>\n        idx &#061; pattern[0] * 4 &#043; pattern[1] * 2 &#043; pattern[2]<br \/>\n        return (rule &gt;&gt; idx) &amp; 1<\/p>\n<p># &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061; \u7b80\u8c31\u5de5\u5177\u51fd\u6570 &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;<\/p>\n<p>def semi_to_jianpu(semi, oct_shift&#061;0):<br \/>\n    &#034;&#034;&#034;\u534a\u97f3 \u2192 \u7b80\u8c31\u8bb0\u53f7&#034;&#034;&#034;<br \/>\n    base &#061; SEMI_MAP.get(semi, &#034;1&#034;)<br \/>\n    has_sharp &#061; &#039;#&#039; in base<\/p>\n<p>    if not has_sharp:<br \/>\n        if oct_shift &gt; 0:<br \/>\n            base &#043;&#061; &#034;&#039;&#034; * oct_shift<br \/>\n        elif oct_shift &lt; 0:<br \/>\n            base &#043;&#061; &#034;.&#034; * (-oct_shift)<br \/>\n    return base<\/p>\n<p>def format_with_group(base_jianpu, dur):<br \/>\n    &#034;&#034;&#034;\u5904\u7406\u62cd\u957f\u548c\u5206\u7ec4\u6807\u8bb0&#034;&#034;&#034;<br \/>\n    if abs(dur &#8211; 1.0) &lt; 0.01:<br \/>\n        return base_jianpu<br \/>\n    if dur &gt; 1.0:<br \/>\n        n &#061; int(round(dur))<br \/>\n        if n &gt;&#061; 2:<br \/>\n            return base_jianpu &#043; &#034;-&#034; * (n &#8211; 1)<\/p>\n<p>    half_count &#061; 0<br \/>\n    d &#061; dur<br \/>\n    while d &lt; 0.999 and half_count &lt; 5:<br \/>\n        d *&#061; 2<br \/>\n        half_count &#043;&#061; 1<br \/>\n    if half_count &gt; 1:<br \/>\n        return &#034;(&#034; &#043; base_jianpu &#043; &#034;)&#034; &#043; &#034;\/&#034; * (half_count &#8211; 1)<br \/>\n    elif half_count &#061;&#061; 1:<br \/>\n        return &#034;(&#034; &#043; base_jianpu &#043; &#034;)&#034;<br \/>\n    return base_jianpu<\/p>\n<p>def melody_to_score(melody, bar_size&#061;4.0):<br \/>\n    &#034;&#034;&#034;\u65cb\u5f8b \u2192 \u6807\u51c6\u7b80\u8c31\u5b57\u7b26\u4e32 (\u5e26 | \u5206\u5c0f\u8282)&#034;&#034;&#034;<br \/>\n    bar_lines &#061; []<br \/>\n    cur_bar &#061; []<br \/>\n    cur_dur &#061; 0.0<\/p>\n<p>    for item in melody:<br \/>\n        if len(item) &gt;&#061; 3:<br \/>\n            semi, octv, dur &#061; item[0], item[1], item[2]<br \/>\n        else:<br \/>\n            continue<br \/>\n        cur_bar.append((semi, octv, dur))<br \/>\n        cur_dur &#043;&#061; dur<br \/>\n        if cur_dur &gt;&#061; bar_size &#8211; 0.001:<br \/>\n            s &#061; &#034;&#034;.join(<br \/>\n                format_with_group(semi_to_jianpu(semi, oo), du)<br \/>\n                for semi, oo, du in cur_bar<br \/>\n            )<br \/>\n            bar_lines.append(s)<br \/>\n            cur_bar &#061; []<br \/>\n            cur_dur &#061; 0.0<\/p>\n<p>    if cur_bar:<br \/>\n        s &#061; &#034;&#034;.join(<br \/>\n            format_with_group(semi_to_jianpu(semi, oo), du)<br \/>\n            for semi, oo, du in cur_bar<br \/>\n        )<br \/>\n        bar_lines.append(s)<\/p>\n<p>    return &#034; | &#034;.join(bar_lines)<\/p>\n<p>def multi_voice_to_score(voices_dict, bar_size&#061;4.0):<br \/>\n    &#034;&#034;&#034;\u591a\u58f0\u90e8 \u2192 \u7b80\u8c31&#034;&#034;&#034;<br \/>\n    lines &#061; []<br \/>\n    for name, melody in voices_dict.items():<br \/>\n        score &#061; melody_to_score(melody, bar_size)<br \/>\n        lines.append(f&#034;\u3010{name}\u3011{score}&#034;)<br \/>\n    return &#034;\\\\n&#034;.join(lines)<\/p>\n<p># &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061; \u57fa\u7840\u65cb\u5f8b\u751f\u6210 &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;<\/p>\n<p>def gen_melody(n_bars, style, oct_lo, oct_hi, step_pref):<br \/>\n    &#034;&#034;&#034;\u751f\u6210\u65cb\u5f8b&#xff0c;\u652f\u6301\u591a\u516b\u5ea6\u53d8\u5316&#034;&#034;&#034;<br \/>\n    scale &#061; style[&#034;scale&#034;]<br \/>\n    chromatic_prob &#061; style.get(&#034;chromatic_prob&#034;, 0.15)<\/p>\n<p>    cur_semi &#061; scale[0]<br \/>\n    cur_oct &#061; 0<br \/>\n    out &#061; []<\/p>\n<p>    for bar in range(n_bars):<br \/>\n        beats &#061; 0.0<br \/>\n        bar_notes &#061; []<br \/>\n        attempts &#061; 0<\/p>\n<p>        while beats &lt; 4.0 and attempts &lt; 50:<br \/>\n            attempts &#043;&#061; 1<br \/>\n            dur &#061; random.choice(style[&#034;rhythms&#034;])<br \/>\n            if beats &#043; dur &gt; 4.0:<br \/>\n                dur &#061; 4.0 &#8211; beats<br \/>\n            if dur &lt; 0.125:<br \/>\n                break<\/p>\n<p>            use_chromatic &#061; random.random() &lt; chromatic_prob<\/p>\n<p>            if use_chromatic:<br \/>\n                non_scale &#061; [s for s in ALL_SEMITONES if s not in scale]<br \/>\n                if non_scale:<br \/>\n                    new_semi &#061; random.choice(non_scale)<br \/>\n                else:<br \/>\n                    new_semi &#061; random.choice(scale)<br \/>\n            else:<br \/>\n                if random.random() &lt; 0.6 and cur_semi in scale:<br \/>\n                    cur_idx &#061; scale.index(cur_semi)<br \/>\n                    step &#061; random.choice([-1, 1]) * random.randint(1, min(step_pref, 3))<br \/>\n                    new_idx &#061; max(0, min(len(scale) &#8211; 1, cur_idx &#043; step))<br \/>\n                    new_semi &#061; scale[new_idx]<br \/>\n                else:<br \/>\n                    new_semi &#061; random.choice(scale)<\/p>\n<p>            if random.random() &lt; 0.15:<br \/>\n                oct_change &#061; random.choice([-2, -1, 0, 0, 0, 1, 2])<br \/>\n                cur_oct &#043;&#061; oct_change<br \/>\n                cur_oct &#061; max(oct_lo, min(oct_hi, cur_oct))<\/p>\n<p>            bar_notes.append((new_semi, cur_oct, dur))<br \/>\n            cur_semi &#061; new_semi<br \/>\n            beats &#043;&#061; dur<\/p>\n<p>        if bar &#061;&#061; 0 and bar_notes:<br \/>\n            bar_notes[0] &#061; (scale[0], 0, bar_notes[0][2])<br \/>\n            cur_semi &#061; scale[0]<br \/>\n            cur_oct &#061; 0<\/p>\n<p>        if bar_notes and random.random() &lt; 0.25:<br \/>\n            bar_notes[-1] &#061; (scale[0], 0, max(0.5, bar_notes[-1][2]))<br \/>\n            cur_semi &#061; scale[0]<br \/>\n            cur_oct &#061; 0<\/p>\n<p>        out.extend(bar_notes)<br \/>\n    return out<\/p>\n<p># &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061; MIDI\u5bfc\u51fa &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;<\/p>\n<p>def export_midi(tracks, bpm&#061;120, filename&#061;&#034;output.mid&#034;, time_signature&#061;(4, 4)):<br \/>\n    &#034;&#034;&#034;\u4f7f\u7528mido\u5bfc\u51faMIDI\u6587\u4ef6&#xff08;\u652f\u6301\u591a\u8f68\u3001\u591a\u901a\u9053\u3001\u5b8c\u6574Meta\u4fe1\u606f&#xff09;<\/p>\n<p>    Args:<br \/>\n        tracks: [(instrument_id, notes), &#8230;]<br \/>\n                notes: [(pitch, duration, time_offset), &#8230;] \u6216 [(pitch, duration), &#8230;]<br \/>\n        bpm: \u901f\u5ea6<br \/>\n        filename: \u8f93\u51fa\u8def\u5f84<br \/>\n        time_signature: (\u5206\u5b50, \u5206\u6bcd)&#xff0c;\u9ed8\u8ba4(4,4)<\/p>\n<p>    Returns:<br \/>\n        (bool, str): (\u662f\u5426\u6210\u529f, \u72b6\u6001\u4fe1\u606f)<br \/>\n    &#034;&#034;&#034;<br \/>\n    try:<br \/>\n        import mido<br \/>\n        from mido import Message, MidiFile, MidiTrack, MetaMessage<br \/>\n    except ImportError:<br \/>\n        return False, &#034;\u9700\u8981\u5b89\u88c5 mido \u5e93: pip install mido&#034;<\/p>\n<p>    try:<br \/>\n        mid &#061; MidiFile(type&#061;1)<br \/>\n        ticks_per_beat &#061; mid.ticks_per_beat<\/p>\n<p>        # \u63a7\u5236\u8f68&#xff1a;\u5168\u5c40Meta\u4fe1\u606f<br \/>\n        control_track &#061; MidiTrack()<br \/>\n        mid.tracks.append(control_track)<br \/>\n        control_track.append(MetaMessage(&#039;time_signature&#039;,<br \/>\n                                          numerator&#061;time_signature[0],<br \/>\n                                          denominator&#061;time_signature[1],<br \/>\n                                          clocks_per_click&#061;24,<br \/>\n                                          notated_32nd_notes_per_beat&#061;8, time&#061;0))<br \/>\n        control_track.append(MetaMessage(&#039;set_tempo&#039;, tempo&#061;mido.bpm2tempo(bpm), time&#061;0))<br \/>\n        control_track.append(MetaMessage(&#039;track_name&#039;, name&#061;&#039;Control&#039;, time&#061;0))<\/p>\n<p>        for track_idx, (inst_id, notes) in enumerate(tracks):<br \/>\n            track &#061; MidiTrack()<br \/>\n            mid.tracks.append(track)<br \/>\n            channel &#061; track_idx % 16<br \/>\n            track.append(MetaMessage(&#039;track_name&#039;, name&#061;f&#039;Track{track_idx&#043;1}&#039;, time&#061;0))<br \/>\n            track.append(Message(&#039;program_change&#039;, program&#061;inst_id % 128,<br \/>\n                                  channel&#061;channel, time&#061;0))<\/p>\n<p>            events &#061; []<br \/>\n            current_time &#061; 0<br \/>\n            for note_data in notes:<br \/>\n                if len(note_data) &gt;&#061; 3:<br \/>\n                    pitch, duration, time_offset &#061; int(note_data[0]), float(note_data[1]), float(note_data[2])<br \/>\n                else:<br \/>\n                    pitch, duration &#061; int(note_data[0]), float(note_data[1])<br \/>\n                    time_offset &#061; current_time<\/p>\n<p>                pitch &#061; max(0, min(127, pitch))<br \/>\n                start_tick &#061; int(time_offset * ticks_per_beat)<br \/>\n                end_tick &#061; int((time_offset &#043; duration) * ticks_per_beat)<\/p>\n<p>                events.append((start_tick, &#039;note_on&#039;, pitch, 100, channel))<br \/>\n                events.append((end_tick, &#039;note_off&#039;, pitch, 0, channel))<br \/>\n                current_time &#061; time_offset &#043; duration<\/p>\n<p>            events.sort(key&#061;lambda x: x[0])<br \/>\n            prev_tick &#061; 0<br \/>\n            for tick, evt_type, pitch, velocity, ch in events:<br \/>\n                delta &#061; max(0, tick &#8211; prev_tick)<br \/>\n                if evt_type &#061;&#061; &#039;note_on&#039;:<br \/>\n                    track.append(Message(&#039;note_on&#039;, note&#061;pitch, velocity&#061;velocity,<br \/>\n                                          channel&#061;ch, time&#061;delta))<br \/>\n                else:<br \/>\n                    track.append(Message(&#039;note_off&#039;, note&#061;pitch, velocity&#061;velocity,<br \/>\n                                          channel&#061;ch, time&#061;delta))<br \/>\n                prev_tick &#061; tick<\/p>\n<p>            track.append(MetaMessage(&#039;end_of_track&#039;, time&#061;0))<\/p>\n<p>        control_track.append(MetaMessage(&#039;end_of_track&#039;, time&#061;0))<br \/>\n        mid.save(filename)<br \/>\n        return True, f&#034;MIDI\u5df2\u5bfc\u51fa: {filename} ({len(tracks)}\u8f68, {bpm}BPM, 4\/4\u62cd)&#034;<br \/>\n    except Exception as e:<br \/>\n        return False, f&#034;\u5bfc\u51fa\u5931\u8d25: {str(e)}&#034;<\/p>\n<p># &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061; \u4e3b\u5e94\u7528 &#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;&#061;<\/p>\n<p>class CompositionApp:<br \/>\n    &#034;&#034;&#034;\u4f5c\u66f2\u8f6f\u4ef6\u4e3b\u5e94\u7528 v3&#034;&#034;&#034;<\/p>\n<p>    COMPOSITION_TECHNIQUES &#061; {<br \/>\n        &#034;\u65cb\u5f8b\u751f\u6210&#034;: &#034;\u57fa\u4e8e\u98ce\u683c\u53c2\u6570\u7684\u968f\u673a\u65cb\u5f8b\u751f\u6210&#034;,<br \/>\n        &#034;\u5bf9\u4f4d\u6cd5&#034;: &#034;\u4e8c\u58f0\u90e8\/\u4e09\u58f0\u90e8\u5bf9\u4f4d\u3001\u5361\u519c\u3001\u8d4b\u683c&#034;,<br \/>\n        &#034;\u548c\u58f0\u5b66&#034;: &#034;\u56db\u90e8\u548c\u58f0\u3001\u7235\u58eb\u548c\u5f26\u8fdb\u884c&#034;,<br \/>\n        &#034;\u66f2\u5f0f\u5b66&#034;: &#034;\u4e8c\u6bb5\u5f0f\u3001\u4e09\u6bb5\u5f0f\u3001\u56de\u65cb\u66f2\u3001\u53d8\u594f\u3001\u594f\u9e23\u66f2&#034;,<br \/>\n        &#034;\u914d\u5668\u6cd5&#034;: &#034;\u4e50\u5668\u5206\u914d\u3001\u7ec7\u4f53\u751f\u6210\u3001\u97f3\u57df\u63a7\u5236&#034;,<br \/>\n        &#034;\u5341\u4e8c\u97f3&#034;: &#034;\u5e8f\u5217\u4e3b\u4e49\u4f5c\u66f2\u3001\u97f3\u5217\u53d8\u5f62&#034;,<br \/>\n        &#034;\u8282\u594f\u6280\u6cd5&#034;: &#034;\u590d\u8282\u594f\u3001\u5207\u5206\u3001\u5f8b\u52a8\u751f\u6210&#034;,<br \/>\n        &#034;\u5076\u7136\u97f3\u4e50&#034;: &#034;\u63b7\u9ab0\u5b50\u4f5c\u66f2\u3001\u5143\u80de\u81ea\u52a8\u673a&#034;,<br \/>\n    }<\/p>\n<p>    def __init__(self, root):<br \/>\n        self.root &#061; root<br \/>\n        root.title(&#034;\u4f5c\u66f2\u8f6f\u4ef6 v3 &#8211; \u7ecf\u5178\u4f5c\u66f2\u6280\u672f\u6574\u5408&#034;)<\/p>\n<p>        screen_width &#061; root.winfo_screenwidth()<br \/>\n        if screen_width &lt; 768:<br \/>\n            root.attributes(&#039;-fullscreen&#039;, True)<br \/>\n            self.font_size &#061; 10<br \/>\n            self.pad_size &#061; 2<br \/>\n            self.is_mobile &#061; True<br \/>\n        else:<br \/>\n            root.geometry(&#034;900&#215;750&#034;)<br \/>\n            self.font_size &#061; 10<br \/>\n            self.pad_size &#061; 4<br \/>\n            self.is_mobile &#061; False<\/p>\n<p>        self._build()<br \/>\n        self._last_score &#061; &#034;&#034;<br \/>\n        self._current_melody &#061; []<br \/>\n        self._midi_tracks &#061; []<\/p>\n<p>    def _build(self):<br \/>\n        main_frame &#061; ttk.Frame(self.root, padding&#061;self.pad_size)<br \/>\n        main_frame.pack(fill&#061;tk.BOTH, expand&#061;True)<\/p>\n<p>        top_frame &#061; ttk.LabelFrame(main_frame, text&#061;&#034;\u4f5c\u66f2\u53c2\u6570&#034;, padding&#061;5)<br \/>\n        top_frame.pack(fill&#061;tk.X, pady&#061;2)<\/p>\n<p>        row1 &#061; ttk.Frame(top_frame)<br \/>\n        row1.pack(fill&#061;tk.X, pady&#061;1)<\/p>\n<p>        ttk.Label(row1, text&#061;&#034;\u8c03\u5f0f:&#034;, font&#061;(&#034;&#034;, self.font_size)).pack(side&#061;tk.LEFT, padx&#061;2)<br \/>\n        self.cmb_key &#061; ttk.Combobox(row1, values&#061;list(KEY_OFFSET.keys()),<br \/>\n                                     width&#061;8, font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.cmb_key.current(7)<br \/>\n        self.cmb_key.pack(side&#061;tk.LEFT, padx&#061;1)<\/p>\n<p>        ttk.Label(row1, text&#061;&#034;\u98ce\u683c:&#034;, font&#061;(&#034;&#034;, self.font_size)).pack(side&#061;tk.LEFT, padx&#061;2)<br \/>\n        self.cmb_style &#061; ttk.Combobox(row1, values&#061;list(STYLES.keys()),<br \/>\n                                       width&#061;8, font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.cmb_style.current(1)<br \/>\n        self.cmb_style.pack(side&#061;tk.LEFT, padx&#061;1)<\/p>\n<p>        ttk.Label(row1, text&#061;&#034;\u5c0f\u8282:&#034;, font&#061;(&#034;&#034;, self.font_size)).pack(side&#061;tk.LEFT, padx&#061;2)<br \/>\n        self.spn_bars &#061; tk.Spinbox(row1, from_&#061;4, to&#061;64, width&#061;4,<br \/>\n                                    font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.spn_bars.delete(0, tk.END)<br \/>\n        self.spn_bars.insert(0, &#034;16&#034;)<br \/>\n        self.spn_bars.pack(side&#061;tk.LEFT, padx&#061;1)<\/p>\n<p>        ttk.Label(row1, text&#061;&#034;BPM:&#034;, font&#061;(&#034;&#034;, self.font_size)).pack(side&#061;tk.LEFT, padx&#061;2)<br \/>\n        self.spn_bpm &#061; tk.Spinbox(row1, from_&#061;30, to&#061;300, width&#061;4,<br \/>\n                                   font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.spn_bpm.delete(0, tk.END)<br \/>\n        self.spn_bpm.insert(0, &#034;120&#034;)<br \/>\n        self.spn_bpm.pack(side&#061;tk.LEFT, padx&#061;1)<\/p>\n<p>        row2 &#061; ttk.Frame(top_frame)<br \/>\n        row2.pack(fill&#061;tk.X, pady&#061;2)<\/p>\n<p>        ttk.Label(row2, text&#061;&#034;\u4f5c\u66f2\u6280\u672f:&#034;, font&#061;(&#034;&#034;, self.font_size)).pack(side&#061;tk.LEFT, padx&#061;2)<br \/>\n        self.cmb_technique &#061; ttk.Combobox(row2, values&#061;list(self.COMPOSITION_TECHNIQUES.keys()),<br \/>\n                                           width&#061;10, font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.cmb_technique.current(0)<br \/>\n        self.cmb_technique.pack(side&#061;tk.LEFT, padx&#061;1)<\/p>\n<p>        ttk.Label(row2, text&#061;&#034;\u5b50\u7c7b\u578b:&#034;, font&#061;(&#034;&#034;, self.font_size)).pack(side&#061;tk.LEFT, padx&#061;2)<br \/>\n        self.cmb_sub &#061; ttk.Combobox(row2, values&#061;[&#034;\u9ed8\u8ba4&#034;], width&#061;10,<br \/>\n                                     font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.cmb_sub.current(0)<br \/>\n        self.cmb_sub.pack(side&#061;tk.LEFT, padx&#061;1)<\/p>\n<p>        ttk.Label(row2, text&#061;&#034;\u53c2\u6570:&#034;, font&#061;(&#034;&#034;, self.font_size)).pack(side&#061;tk.LEFT, padx&#061;2)<br \/>\n        self.cmb_param &#061; ttk.Combobox(row2, values&#061;[&#034;\u9ed8\u8ba4&#034;], width&#061;8,<br \/>\n                                       font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.cmb_param.current(0)<br \/>\n        self.cmb_param.pack(side&#061;tk.LEFT, padx&#061;1)<\/p>\n<p>        self.cmb_technique.bind(&#034;&lt;&lt;ComboboxSelected&gt;&gt;&#034;, self._update_subtypes)<\/p>\n<p>        btn_frame &#061; ttk.Frame(top_frame)<br \/>\n        btn_frame.pack(fill&#061;tk.X, pady&#061;3)<\/p>\n<p>        self.btn_generate &#061; ttk.Button(btn_frame, text&#061;&#034;\u751f\u6210\u4f5c\u54c1&#034;,<br \/>\n                                         command&#061;self._do_async, width&#061;10)<br \/>\n        self.btn_generate.pack(side&#061;tk.LEFT, padx&#061;3)<\/p>\n<p>        ttk.Button(btn_frame, text&#061;&#034;\u518d\u6765\u4e00\u9996&#034;,<br \/>\n                   command&#061;self._do_again, width&#061;10).pack(side&#061;tk.LEFT, padx&#061;3)<\/p>\n<p>        ttk.Button(btn_frame, text&#061;&#034;\u4fdd\u5b58\u7b80\u8c31&#034;,<br \/>\n                   command&#061;self._save, width&#061;10).pack(side&#061;tk.LEFT, padx&#061;3)<\/p>\n<p>        ttk.Button(btn_frame, text&#061;&#034;\u590d\u5236&#034;,<br \/>\n                   command&#061;self._copy, width&#061;8).pack(side&#061;tk.LEFT, padx&#061;3)<\/p>\n<p>        ttk.Button(btn_frame, text&#061;&#034;\u5bfc\u51faMIDI&#034;,<br \/>\n                   command&#061;self._export_midi, width&#061;10).pack(side&#061;tk.LEFT, padx&#061;3)<\/p>\n<p>        self.lbl_status &#061; ttk.Label(btn_frame, text&#061;&#034;\u5c31\u7eea&#034;,<br \/>\n                                     foreground&#061;&#034;green&#034;, font&#061;(&#034;&#034;, self.font_size))<br \/>\n        self.lbl_status.pack(side&#061;tk.LEFT, padx&#061;5)<\/p>\n<p>        txt_height &#061; 20 if self.is_mobile else 25<br \/>\n        self.txt &#061; scrolledtext.ScrolledText(main_frame,<br \/>\n                                              font&#061;(&#034;Consolas&#034;, 13),<br \/>\n                                              wrap&#061;tk.WORD,<br \/>\n                                              height&#061;txt_height)<br \/>\n        self.txt.pack(fill&#061;tk.BOTH, expand&#061;True, pady&#061;2)<\/p>\n<p>        bot &#061; ttk.Frame(main_frame, padding&#061;2)<br \/>\n        bot.pack(fill&#061;tk.X)<\/p>\n<p>        tip &#061; &#034;\u4f5c\u66f2\u6280\u672f: \u65cb\u5f8b|\u5bf9\u4f4d|\u548c\u58f0|\u66f2\u5f0f|\u914d\u5668|\u5341\u4e8c\u97f3|\u8282\u594f|\u5076\u7136\u97f3\u4e50 | \u8f93\u51fa: \u591a\u58f0\u90e8\u7b80\u8c31 &#043; MIDI&#034;<br \/>\n        ttk.Label(bot, text&#061;tip, foreground&#061;&#034;gray&#034;,<br \/>\n                   font&#061;(&#034;&#034;, self.font_size &#8211; 1 if self.is_mobile else self.font_size),<br \/>\n                   wraplength&#061;800).pack(side&#061;tk.LEFT, padx&#061;4)<\/p>\n<p>    def _update_subtypes(self, event&#061;None):<br \/>\n        tech &#061; self.cmb_technique.get()<\/p>\n<p>        subtypes &#061; {<br \/>\n            &#034;\u65cb\u5f8b\u751f\u6210&#034;: [&#034;\u5355\u65cb\u5f8b&#034;, &#034;\u591a\u58f0\u90e8\u65cb\u5f8b&#034;],<br \/>\n            &#034;\u5bf9\u4f4d\u6cd5&#034;: [&#034;\u4e8c\u58f0\u90e8\u5bf9\u4f4d&#034;, &#034;\u4e09\u58f0\u90e8\u5bf9\u4f4d&#034;, &#034;\u5361\u519c&#034;, &#034;\u8d4b\u683c\u4e3b\u9898&#034;],<br \/>\n            &#034;\u548c\u58f0\u5b66&#034;: [&#034;\u56db\u90e8\u548c\u58f0&#034;, &#034;\u7235\u58eb\u548c\u5f26&#034;, &#034;\u529f\u80fd\u548c\u58f0\u8fdb\u884c&#034;],<br \/>\n            &#034;\u66f2\u5f0f\u5b66&#034;: [&#034;\u4e8c\u6bb5\u5f0f&#034;, &#034;\u4e09\u6bb5\u5f0f&#034;, &#034;\u56de\u65cb\u66f2&#034;, &#034;\u53d8\u594f\u66f2&#034;, &#034;\u594f\u9e23\u66f2\u5f0f&#034;],<br \/>\n            &#034;\u914d\u5668\u6cd5&#034;: [&#034;\u5f26\u4e50\u56db\u91cd\u594f&#034;, &#034;\u94a2\u7434\u4e09\u91cd\u594f&#034;, &#034;\u7ba1\u5f26\u4e50\u961f&#034;, &#034;\u6d41\u884c\u4e50\u961f&#034;, &#034;\u7ec7\u4f53\u751f\u6210&#034;],<br \/>\n            &#034;\u5341\u4e8c\u97f3&#034;: [&#034;\u57fa\u7840\u5e8f\u5217&#034;, &#034;\u5b8c\u6574\u4f5c\u54c1&#034;, &#034;\u97f3\u5217\u77e9\u9635&#034;],<br \/>\n            &#034;\u8282\u594f\u6280\u6cd5&#034;: [&#034;\u590d\u8282\u594f&#034;, &#034;\u5207\u5206\u8282\u594f&#034;, &#034;\u5f8b\u52a8\u751f\u6210&#034;, &#034;\u8282\u594f\u52a8\u673a\u53d1\u5c55&#034;],<br \/>\n            &#034;\u5076\u7136\u97f3\u4e50&#034;: [&#034;\u63b7\u9ab0\u5b50\u4f5c\u66f2&#034;, &#034;\u5b8c\u5168\u968f\u673a&#034;, &#034;\u5143\u80de\u81ea\u52a8\u673a&#034;],<br \/>\n        }<\/p>\n<p>        self.cmb_sub[&#039;values&#039;] &#061; subtypes.get(tech, [&#034;\u9ed8\u8ba4&#034;])<br \/>\n        self.cmb_sub.current(0)<\/p>\n<p>        params &#061; {<br \/>\n            &#034;\u5bf9\u4f4d\u6cd5&#034;: [&#034;\u4e00\u97f3\u5bf9\u4e00\u97f3&#034;, &#034;\u4e8c\u97f3\u5bf9\u4e00\u97f3&#034;, &#034;\u534e\u5f69\u5bf9\u4f4d&#034;],<br \/>\n            &#034;\u66f2\u5f0f\u5b66&#034;: [&#034;8\u5c0f\u8282&#034;, &#034;16\u5c0f\u8282&#034;, &#034;32\u5c0f\u8282&#034;],<br \/>\n            &#034;\u914d\u5668\u6cd5&#034;: [&#034;\u4e3b\u8c03\u7ec7\u4f53&#034;, &#034;\u590d\u8c03\u7ec7\u4f53&#034;, &#034;\u5206\u89e3\u548c\u5f26&#034;, &#034;\u6301\u7eed\u97f3&#034;],<br \/>\n            &#034;\u5341\u4e8c\u97f3&#034;: [&#034;\u5e26\u8282\u594f&#034;, &#034;\u7eaf\u97f3\u5217&#034;, &#034;\u77e9\u9635\u5c55\u793a&#034;],<br \/>\n            &#034;\u8282\u594f\u6280\u6cd5&#034;: [&#034;3\u5bf92&#034;, &#034;4\u5bf93&#034;, &#034;5\u5bf94&#034;, &#034;\u5207\u5206&#034;, &#034;Shuffle&#034;, &#034;Funk&#034;, &#034;Bossa&#034;],<br \/>\n        }<br \/>\n        self.cmb_param[&#039;values&#039;] &#061; params.get(tech, [&#034;\u9ed8\u8ba4&#034;])<br \/>\n        self.cmb_param.current(0)<\/p>\n<p>    def _do_async(self):<br \/>\n        self.btn_generate.config(state&#061;tk.DISABLED)<br \/>\n        self.lbl_status.config(text&#061;&#034;\u751f\u6210\u4e2d&#8230;&#034;, foreground&#061;&#034;orange&#034;)<br \/>\n        Thread(target&#061;self._do, daemon&#061;True).start()<\/p>\n<p>    def _do(self):<br \/>\n        try:<br \/>\n            key &#061; self.cmb_key.get()<br \/>\n            style_name &#061; self.cmb_style.get()<br \/>\n            technique &#061; self.cmb_technique.get()<br \/>\n            subtype &#061; self.cmb_sub.get()<br \/>\n            param &#061; self.cmb_param.get()<\/p>\n<p>            try:<br \/>\n                bars &#061; int(self.spn_bars.get())<br \/>\n            except:<br \/>\n                bars &#061; 16<br \/>\n            try:<br \/>\n                bpm &#061; int(self.spn_bpm.get())<br \/>\n            except:<br \/>\n                bpm &#061; 120<\/p>\n<p>            style &#061; STYLES.get(style_name, STYLES[&#034;\u6292\u60c5&#034;])<br \/>\n            key_offset &#061; KEY_OFFSET.get(key, 0)<\/p>\n<p>            if key.endswith(&#034;\u5c0f\u8c03&#034;):<br \/>\n                scale &#061; MINOR_SCALE<br \/>\n            elif style_name in (&#034;\u5e73\u9759&#034;, &#034;\u53e4\u98ce&#034;):<br \/>\n                scale &#061; PENTATONIC_MAJOR<br \/>\n            elif style_name &#061;&#061; &#034;\u84dd\u8c03&#034;:<br \/>\n                scale &#061; BLUES_SCALE<br \/>\n            elif style_name &#061;&#061; &#034;\u5168\u97f3\u9636&#034;:<br \/>\n                scale &#061; WHOLE_TONE<br \/>\n            elif style_name &#061;&#061; &#034;\u516b\u58f0\u97f3\u9636&#034;:<br \/>\n                scale &#061; OCTATONIC<br \/>\n            else:<br \/>\n                scale &#061; MAJOR_SCALE<\/p>\n<p>            oct_lo, oct_hi &#061; style[&#034;oct_range&#034;]<br \/>\n            melody &#061; gen_melody(bars, style, oct_lo, oct_hi, style[&#034;step_pref&#034;])<br \/>\n            self._current_melody &#061; melody<\/p>\n<p>            result &#061; self._apply_technique(technique, subtype, param, melody,<br \/>\n                                           key_offset, scale, style, bars)<\/p>\n<p>            header &#061; f&#034;\u3010{technique} &#8211; {subtype}\u3011 | \u98ce\u683c: {style_name}\\\\n&#034;<br \/>\n            header &#043;&#061; &#034;&#061;&#034; * 50 &#043; &#034;\\\\n&#034;<\/p>\n<p>            header &#043;&#061; f&#034;[0,{bpm},{key}]\\\\n&#034;<br \/>\n            full &#061; header &#043; result<\/p>\n<p>            self._last_score &#061; full<br \/>\n            self.root.after(0, self._render, full)<\/p>\n<p>        except Exception as e:<br \/>\n            self.root.after(0, self._render, f&#034;\u9519\u8bef: {str(e)}&#034;)<\/p>\n<p>    def _melody_to_midi_notes(self, melody):<br \/>\n        &#034;&#034;&#034;\u5c06\u65cb\u5f8b\u6570\u636e\u8f6c\u4e3aMIDI\u97f3\u7b26\u5217\u8868 [(pitch, duration, time_offset), &#8230;]&#034;&#034;&#034;<br \/>\n        notes &#061; []<br \/>\n        time &#061; 0<br \/>\n        for item in melody:<br \/>\n            if len(item) &gt;&#061; 3:<br \/>\n                semi, octv, dur &#061; item[0], item[1], item[2]<br \/>\n            else:<br \/>\n                continue<br \/>\n            pitch &#061; (4 &#043; octv) * 12 &#043; semi<br \/>\n            notes.append((pitch, dur, time))<br \/>\n            time &#043;&#061; dur<br \/>\n        return notes<\/p>\n<p>    def _build_tracks_from_voices(self, voices_dict, instrument_map&#061;None):<br \/>\n        &#034;&#034;&#034;\u4ece\u591a\u58f0\u90e8\u5b57\u5178\u6784\u5efaMIDI\u8f68\u9053\u5217\u8868&#034;&#034;&#034;<br \/>\n        defaults &#061; [0, 32, 40, 48, 56, 60, 73, 80]<br \/>\n        tracks &#061; []<br \/>\n        for i, (name, melody) in enumerate(voices_dict.items()):<br \/>\n            inst &#061; instrument_map.get(name, defaults[i % len(defaults)]) if instrument_map else defaults[i % len(defaults)]<br \/>\n            tracks.append((inst, self._melody_to_midi_notes(melody)))<br \/>\n        return tracks<\/p>\n<p>    def _apply_technique(self, technique, subtype, param, melody, key_offset, scale, style, bars):<br \/>\n        &#034;&#034;&#034;\u5e94\u7528\u9009\u5b9a\u7684\u4f5c\u66f2\u6280\u672f&#xff0c;\u540c\u65f6\u4fdd\u5b58MIDI\u591a\u8f68\u6570\u636e&#034;&#034;&#034;<br \/>\n        self._midi_tracks &#061; []<\/p>\n<p>        if technique &#061;&#061; &#034;\u65cb\u5f8b\u751f\u6210&#034;:<br \/>\n            self._midi_tracks &#061; [(0, self._melody_to_midi_notes(melody))]<br \/>\n            if subtype &#061;&#061; &#034;\u5355\u65cb\u5f8b&#034;:<br \/>\n                return melody_to_score(melody)<br \/>\n            else:<br \/>\n                melody2 &#061; gen_melody(bars, style, -1, 1, style[&#034;step_pref&#034;])<br \/>\n                self._midi_tracks &#061; [<br \/>\n                    (0, self._melody_to_midi_notes(melody)),<br \/>\n                    (48, self._melody_to_midi_notes(melody2)),<br \/>\n                ]<br \/>\n                return multi_voice_to_score({&#034;\u65cb\u5f8b1&#034;: melody, &#034;\u65cb\u5f8b2&#034;: melody2})<\/p>\n<p>        elif technique &#061;&#061; &#034;\u5bf9\u4f4d\u6cd5&#034;:<br \/>\n            cp &#061; CounterpointEngine(key_offset, scale)<\/p>\n<p>            if subtype &#061;&#061; &#034;\u4e8c\u58f0\u90e8\u5bf9\u4f4d&#034;:<br \/>\n                cantus &#061; [semi &#043; (4 &#043; octv) * 12 for semi, octv, dur in melody]<br \/>\n                counter &#061; cp.generate_counterpoint(cantus, &#034;above&#034;, 1)<br \/>\n                counter_melody &#061; [(c % 12, c \/\/ 12 &#8211; 4, 1.0) for c in counter[:len(melody)]]<br \/>\n                self._midi_tracks &#061; [<br \/>\n                    (0, self._melody_to_midi_notes(melody)),<br \/>\n                    (40, self._melody_to_midi_notes(counter_melody)),<br \/>\n                ]<br \/>\n                return multi_voice_to_score({&#034;\u5b9a\u65cb\u5f8b&#034;: melody, &#034;\u5bf9\u4f4d\u58f0\u90e8&#034;: counter_melody})<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u4e09\u58f0\u90e8\u5bf9\u4f4d&#034;:<br \/>\n                cantus &#061; [semi &#043; (4 &#043; octv) * 12 for semi, octv, dur in melody]<br \/>\n                counter1 &#061; cp.generate_counterpoint(cantus, &#034;above&#034;, 1)<br \/>\n                counter2 &#061; cp.generate_counterpoint(cantus, &#034;below&#034;, 1)<br \/>\n                c1 &#061; [(c % 12, c \/\/ 12 &#8211; 4, 1.0) for c in counter1[:len(melody)]]<br \/>\n                c2 &#061; [(c % 12, c \/\/ 12 &#8211; 4, 1.0) for c in counter2[:len(melody)]]<br \/>\n                self._midi_tracks &#061; [<br \/>\n                    (40, self._melody_to_midi_notes(c1)),<br \/>\n                    (0, self._melody_to_midi_notes(melody)),<br \/>\n                    (32, self._melody_to_midi_notes(c2)),<br \/>\n                ]<br \/>\n                return multi_voice_to_score({&#034;\u9ad8\u97f3&#034;: c1, &#034;\u4e2d\u97f3&#034;: melody, &#034;\u4f4e\u97f3&#034;: c2})<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u5361\u519c&#034;:<br \/>\n                cantus &#061; [semi &#043; (4 &#043; octv) * 12 for semi, octv, dur in melody[:16]]<br \/>\n                voices &#061; cp.generate_canon(cantus, interval&#061;7, delay&#061;2)<br \/>\n                v1 &#061; [(v % 12, v \/\/ 12 &#8211; 4, 1.0) for v in voices[0]]<br \/>\n                v2 &#061; [(v % 12, v \/\/ 12 &#8211; 4, 1.0) for v in voices[1]]<br \/>\n                self._midi_tracks &#061; [<br \/>\n                    (0, self._melody_to_midi_notes(v1)),<br \/>\n                    (48, self._melody_to_midi_notes(v2)),<br \/>\n                ]<br \/>\n                header &#061; &#034;\u3010\u5361\u519c \u2014 \u516b\u5ea6\u6a21\u4eff&#xff0c;\u5ef6\u8fdf2\u62cd\u3011&#034;<br \/>\n                return header &#043; &#034;\\\\n&#034; &#043; multi_voice_to_score({&#034;\u5148\u5bfc&#034;: v1, &#034;\u6a21\u4eff&#034;: v2})<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u8d4b\u683c\u4e3b\u9898&#034;:<br \/>\n                subject &#061; cp.generate_fugue_subject(scale, length&#061;12)<br \/>\n                subject_melody &#061; [(s % 12, 0, 1.0) for s in subject]<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(subject_melody))]<br \/>\n                return &#034;\u3010\u8d4b\u683c\u4e3b\u9898\u3011\\\\n&#034; &#043; melody_to_score(subject_melody) &#043; &#034;\\\\n\\\\n&#034;<\/p>\n<p>        elif technique &#061;&#061; &#034;\u548c\u58f0\u5b66&#034;:<br \/>\n            he &#061; HarmonyEngine(key_offset, scale)<\/p>\n<p>            if subtype &#061;&#061; &#034;\u56db\u90e8\u548c\u58f0&#034;:<br \/>\n                progression &#061; random.choice(list(he.PROGRESSIONS.keys()))<br \/>\n                voices &#061; he.generate_four_part(progression, n_bars&#061;min(bars, 8))<br \/>\n                result &#061; &#034;\u3010\u56db\u90e8\u548c\u58f0 \u2014 \u548c\u5f26\u8fdb\u884c: &#034; &#043; progression &#043; &#034;\u3011\\\\n&#034;<br \/>\n                inst_map &#061; {&#039;S&#039;: 0, &#039;A&#039;: 48, &#039;T&#039;: 56, &#039;B&#039;: 32}<br \/>\n                tracks &#061; []<br \/>\n                for v_name in [&#039;S&#039;, &#039;A&#039;, &#039;T&#039;, &#039;B&#039;]:<br \/>\n                    voice_melody &#061; [(p % 12, p \/\/ 12 &#8211; 4, 1.0) for p in voices[v_name]]<br \/>\n                    tracks.append((inst_map[v_name], self._melody_to_midi_notes(voice_melody)))<br \/>\n                    result &#043;&#061; &#034;\u3010&#034; &#043; v_name &#043; &#034;\u3011&#034; &#043; melody_to_score(voice_melody) &#043; &#034;\\\\n&#034;<br \/>\n                self._midi_tracks &#061; tracks<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u7235\u58eb\u548c\u5f26&#034;:<br \/>\n                changes &#061; he.generate_jazz_changes(n_bars&#061;min(bars, 8))<br \/>\n                result &#061; &#034;\u3010\u7235\u58eb\u548c\u5f26\u8fdb\u884c\u3011\\\\n&#034;<br \/>\n                for i, ch in enumerate(changes):<br \/>\n                    result &#043;&#061; &#034;\u7b2c&#034; &#043; str(i&#043;1) &#043; &#034;\u5c0f\u8282: &#034; &#043; ch &#043; &#034;  &#034;<br \/>\n                    if (i &#043; 1) % 4 &#061;&#061; 0:<br \/>\n                        result &#043;&#061; &#034;\\\\n&#034;<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(melody))]<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u529f\u80fd\u548c\u58f0\u8fdb\u884c&#034;:<br \/>\n                progression &#061; random.choice(list(he.PROGRESSIONS.keys()))<br \/>\n                prog &#061; he.PROGRESSIONS[progression]<br \/>\n                result &#061; &#034;\u3010\u529f\u80fd\u548c\u58f0 \u2014 &#034; &#043; progression &#043; &#034;\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; &#034; \u2192 &#034;.join(prog) &#043; &#034;\\\\n&#034;<br \/>\n                chord_melody &#061; []<br \/>\n                for chord_name in prog * (bars \/\/ len(prog) &#043; 1):<br \/>\n                    root &#061; he.CHORD_TYPES[chord_name][0]<br \/>\n                    chord_melody.append(((root &#043; key_offset) % 12, 0, 1.0))<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(chord_melody[:bars]))]<br \/>\n                result &#043;&#061; &#034;\\\\n\u6839\u97f3\u8fdb\u884c: &#034; &#043; melody_to_score(chord_melody[:bars])<br \/>\n                return result<\/p>\n<p>        elif technique &#061;&#061; &#034;\u66f2\u5f0f\u5b66&#034;:<br \/>\n            fe &#061; FormEngine()<\/p>\n<p>            if subtype &#061;&#061; &#034;\u4e8c\u6bb5\u5f0f&#034;:<br \/>\n                form &#061; fe.generate_binary(melody, n_bars_a&#061;bars\/\/2, n_bars_b&#061;bars\/\/2)<br \/>\n                self._midi_tracks &#061; self._build_tracks_from_voices(form)<br \/>\n                return &#034;\u3010\u4e8c\u6bb5\u5f0f A-B\u3011\\\\n&#034; &#043; multi_voice_to_score(form)<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u4e09\u6bb5\u5f0f&#034;:<br \/>\n                form &#061; fe.generate_ternary(melody, n_bars&#061;bars\/\/3)<br \/>\n                self._midi_tracks &#061; self._build_tracks_from_voices(form)<br \/>\n                return &#034;\u3010\u4e09\u6bb5\u5f0f A-B-A&#039;\u3011\\\\n&#034; &#043; multi_voice_to_score(form)<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u56de\u65cb\u66f2&#034;:<br \/>\n                form &#061; fe.generate_rondo(melody, episodes&#061;2)<br \/>\n                self._midi_tracks &#061; self._build_tracks_from_voices(form)<br \/>\n                return &#034;\u3010\u56de\u65cb\u66f2 A-B-A-C-A\u3011\\\\n&#034; &#043; multi_voice_to_score(form)<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u53d8\u594f\u66f2&#034;:<br \/>\n                n_var &#061; 3 if param &#061;&#061; &#034;\u9ed8\u8ba4&#034; else int(param[0]) if param[0].isdigit() else 3<br \/>\n                form &#061; fe.generate_variations(melody, n_variations&#061;n_var)<br \/>\n                self._midi_tracks &#061; self._build_tracks_from_voices(form)<br \/>\n                return &#034;\u3010\u53d8\u594f\u66f2\u3011\\\\n&#034; &#043; multi_voice_to_score(form)<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u594f\u9e23\u66f2\u5f0f&#034;:<br \/>\n                melody2 &#061; gen_melody(bars, style, -1, 2, style[&#034;step_pref&#034;])<br \/>\n                form &#061; fe.generate_sonata(melody, melody2, n_bars&#061;bars)<br \/>\n                result &#061; &#034;\u3010\u594f\u9e23\u66f2\u5f0f\u3011\\\\n&#034;<br \/>\n                all_tracks &#061; []<br \/>\n                defaults &#061; [0, 48, 40, 56]<br \/>\n                idx &#061; 0<br \/>\n                for section_name, section in form[&#034;\u5448\u793a\u90e8&#034;].items():<br \/>\n                    result &#043;&#061; &#034;&#8212; &#034; &#043; section_name &#043; &#034; &#8212;\\\\n&#034;<br \/>\n                    result &#043;&#061; &#034;\u3010&#034; &#043; section_name &#043; &#034;\u3011&#034; &#043; melody_to_score(section) &#043; &#034;\\\\n&#034;<br \/>\n                    all_tracks.append((defaults[idx % len(defaults)], self._melody_to_midi_notes(section)))<br \/>\n                    idx &#043;&#061; 1<br \/>\n                result &#043;&#061; &#034;\\\\n&#8212; \u5c55\u5f00\u90e8 &#8212;\\\\n&#034;<br \/>\n                result &#043;&#061; melody_to_score(form[&#034;\u5c55\u5f00\u90e8&#034;]) &#043; &#034;\\\\n&#034;<br \/>\n                all_tracks.append((defaults[idx % len(defaults)], self._melody_to_midi_notes(form[&#034;\u5c55\u5f00\u90e8&#034;])))<br \/>\n                idx &#043;&#061; 1<br \/>\n                for section_name, section in form[&#034;\u518d\u73b0\u90e8&#034;].items():<br \/>\n                    result &#043;&#061; &#034;&#8212; &#034; &#043; section_name &#043; &#034; &#8212;\\\\n&#034;<br \/>\n                    result &#043;&#061; &#034;\u3010&#034; &#043; section_name &#043; &#034;\u3011&#034; &#043; melody_to_score(section) &#043; &#034;\\\\n&#034;<br \/>\n                    all_tracks.append((defaults[idx % len(defaults)], self._melody_to_midi_notes(section)))<br \/>\n                    idx &#043;&#061; 1<br \/>\n                self._midi_tracks &#061; all_tracks<br \/>\n                return result<\/p>\n<p>        elif technique &#061;&#061; &#034;\u914d\u5668\u6cd5&#034;:<br \/>\n            oe &#061; OrchestrationEngine()<\/p>\n<p>            if subtype in [&#034;\u5f26\u4e50\u56db\u91cd\u594f&#034;, &#034;\u94a2\u7434\u4e09\u91cd\u594f&#034;, &#034;\u7ba1\u5f26\u4e50\u961f&#034;, &#034;\u6d41\u884c\u4e50\u961f&#034;]:<br \/>\n                tracks_data &#061; oe.assign_instruments(melody, subtype)<br \/>\n                result &#061; &#034;\u3010&#034; &#043; subtype &#043; &#034;\u3011\\\\n&#034;<br \/>\n                midi_tracks &#061; []<br \/>\n                for i, track in enumerate(tracks_data):<br \/>\n                    track_melody &#061; [(t[0], t[1], t[2]) for t in track]<br \/>\n                    inst_name &#061; track[0][4] if track else &#034;\u672a\u77e5&#034;<br \/>\n                    inst_id &#061; track[0][3] if track else 0<br \/>\n                    result &#043;&#061; &#034;\u3010&#034; &#043; inst_name &#043; &#034;\u3011&#034; &#043; melody_to_score(track_melody) &#043; &#034;\\\\n&#034;<br \/>\n                    midi_tracks.append((inst_id, self._melody_to_midi_notes(track_melody)))<br \/>\n                self._midi_tracks &#061; midi_tracks<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u7ec7\u4f53\u751f\u6210&#034;:<br \/>\n                texture_type &#061; param if param in [&#034;\u4e3b\u8c03&#034;, &#034;\u590d\u8c03&#034;, &#034;\u5206\u89e3\u548c\u5f26&#034;, &#034;\u6301\u7eed\u97f3&#034;] else &#034;\u4e3b\u8c03&#034;<br \/>\n                texture &#061; oe.generate_texture(melody, texture_type)<br \/>\n                result &#061; &#034;\u3010&#034; &#043; texture_type &#043; &#034;\u7ec7\u4f53\u3011\\\\n&#034;<br \/>\n                midi_tracks &#061; []<br \/>\n                defaults &#061; [0, 48, 40, 32]<br \/>\n                idx &#061; 0<br \/>\n                for name, content in texture.items():<br \/>\n                    if name &#061;&#061; &#034;\u4f34\u594f&#034;:<br \/>\n                        result &#043;&#061; &#034;\u3010&#034; &#043; name &#043; &#034;\u3011&#xff08;\u548c\u5f26\u4f34\u594f&#xff09;\\\\n&#034;<br \/>\n                        if content and isinstance(content[0], list):<br \/>\n                            flat &#061; []<br \/>\n                            for chord_notes in content:<br \/>\n                                if chord_notes:<br \/>\n                                    flat.extend(chord_notes)<br \/>\n                            midi_tracks.append((defaults[idx % len(defaults)], self._melody_to_midi_notes(flat)))<br \/>\n                        else:<br \/>\n                            midi_tracks.append((defaults[idx % len(defaults)], self._melody_to_midi_notes(content)))<br \/>\n                    else:<br \/>\n                        result &#043;&#061; &#034;\u3010&#034; &#043; name &#043; &#034;\u3011&#034; &#043; melody_to_score(content) &#043; &#034;\\\\n&#034;<br \/>\n                        midi_tracks.append((defaults[idx % len(defaults)], self._melody_to_midi_notes(content)))<br \/>\n                    idx &#043;&#061; 1<br \/>\n                self._midi_tracks &#061; midi_tracks<br \/>\n                return result<\/p>\n<p>        elif technique &#061;&#061; &#034;\u5341\u4e8c\u97f3&#034;:<br \/>\n            tte &#061; TwelveToneEngine()<\/p>\n<p>            if subtype &#061;&#061; &#034;\u57fa\u7840\u5e8f\u5217&#034;:<br \/>\n                row &#061; tte.generate_row()<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes([(r, 0, 0.5) for r in row]))]<br \/>\n                return &#034;\u3010\u5341\u4e8c\u97f3\u57fa\u7840\u5e8f\u5217\u3011\\\\n&#034; &#043; &#034; &#034;.join(str(x) for x in row) &#043; &#034;\\\\n&#034;<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u5b8c\u6574\u4f5c\u54c1&#034;:<br \/>\n                piece &#061; tte.generate_piece(n_notes&#061;min(bars * 4, 48))<br \/>\n                transforms &#061; tte.get_transformations()<br \/>\n                result &#061; &#034;\u3010\u5341\u4e8c\u97f3\u6280\u6cd5\u4f5c\u54c1\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;P:  &#034; &#043; &#034; &#034;.join(str(x) for x in transforms[&#039;P&#039;]) &#043; &#034;\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;I:  &#034; &#043; &#034; &#034;.join(str(x) for x in transforms[&#039;I&#039;]) &#043; &#034;\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;R:  &#034; &#043; &#034; &#034;.join(str(x) for x in transforms[&#039;R&#039;]) &#043; &#034;\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;RI: &#034; &#043; &#034; &#034;.join(str(x) for x in transforms[&#039;RI&#039;]) &#043; &#034;\\\\n\\\\n&#034;<br \/>\n                result &#043;&#061; melody_to_score(piece)<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(piece))]<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u97f3\u5217\u77e9\u9635&#034;:<br \/>\n                tte.generate_row()<br \/>\n                matrix &#061; tte.build_matrix()<br \/>\n                result &#061; &#034;\u3010\u5341\u4e8c\u97f3\u97f3\u5217\u77e9\u9635\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;    &#034; &#043; &#034; &#034;.join(f&#034;{x:2d}&#034; for x in range(12)) &#043; &#034;\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;   &#034; &#043; &#034;-&#034; * 37 &#043; &#034;\\\\n&#034;<br \/>\n                for i, row in enumerate(matrix):<br \/>\n                    result &#043;&#061; f&#034;{i:2d}&#034; &#043; &#034; | &#034; &#043; &#034; &#034;.join(f&#034;{x:2d}&#034; for x in row) &#043; &#034;\\\\n&#034;<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes([(r, 0, 0.5) for r in tte.row]))]<br \/>\n                return result<\/p>\n<p>        elif technique &#061;&#061; &#034;\u8282\u594f\u6280\u6cd5&#034;:<br \/>\n            re &#061; RhythmEngine()<\/p>\n<p>            if subtype &#061;&#061; &#034;\u590d\u8282\u594f&#034;:<br \/>\n                if &#034;\u5bf9&#034; in param:<br \/>\n                    parts &#061; param.split(&#034;\u5bf9&#034;)<br \/>\n                    p1, p2 &#061; int(parts[0]), int(parts[1])<br \/>\n                else:<br \/>\n                    p1, p2 &#061; 3, 2<br \/>\n                poly &#061; re.generate_polyrhythm(p1, p2, n_beats&#061;4)<br \/>\n                result &#061; &#034;\u3010\u590d\u8282\u594f &#034; &#043; str(p1) &#043; &#034;\u5bf9&#034; &#043; str(p2) &#043; &#034;\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;\u57fa\u7840\u62cd: &#034; &#043; &#034; | &#034;.join([f&#034;{n:.2f}&#034; for n, d in poly[&#034;\u57fa\u7840&#034;]]) &#043; &#034;\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;\u5bf9\u4f4d\u62cd: &#034; &#043; &#034; | &#034;.join([f&#034;{n:.2f}&#034; for n, d in poly[&#034;\u5bf9\u4f4d&#034;]]) &#043; &#034;\\\\n&#034;<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(melody))]<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u5207\u5206\u8282\u594f&#034;:<br \/>\n                base_pattern &#061; [1.0, 1.0, 1.0, 1.0]<br \/>\n                syncopated &#061; re.generate_syncopation(base_pattern, intensity&#061;0.6)<br \/>\n                result &#061; &#034;\u3010\u5207\u5206\u8282\u594f\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;\u539f\u59cb: &#034; &#043; &#034; | &#034;.join([f&#034;{d:.2f}&#034; for d in base_pattern]) &#043; &#034;\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;\u5207\u5206: &#034; &#043; &#034; | &#034;.join([f&#034;{d:.2f}&#034; for d in syncopated]) &#043; &#034;\\\\n&#034;<br \/>\n                sync_melody &#061; []<br \/>\n                for i, (semi, octv, _) in enumerate(melody[:len(syncopated)]):<br \/>\n                    sync_melody.append((semi, octv, syncopated[i]))<br \/>\n                result &#043;&#061; &#034;\\\\n\u65cb\u5f8b: &#034; &#043; melody_to_score(sync_melody)<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(sync_melody))]<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u5f8b\u52a8\u751f\u6210&#034;:<br \/>\n                groove_style &#061; param.lower() if param !&#061; &#034;\u9ed8\u8ba4&#034; else &#034;funk&#034;<br \/>\n                groove &#061; re.generate_groove(groove_style, n_bars&#061;bars \/\/ 4)<br \/>\n                result &#061; &#034;\u3010&#034; &#043; groove_style.title() &#043; &#034; \u5f8b\u52a8\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;\u8282\u594f\u578b: &#034; &#043; &#034; &#034;.join([f&#034;{d:.2f}&#034; for d in groove[:16]]) &#043; &#034;&#8230;\\\\n&#034;<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(melody))]<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u8282\u594f\u52a8\u673a\u53d1\u5c55&#034;:<br \/>\n                motif &#061; [1.0, 0.5, 0.5]<br \/>\n                techniques &#061; [&#034;\u91cd\u590d&#034;, &#034;\u589e\u503c&#034;, &#034;\u51cf\u503c&#034;, &#034;\u9006\u884c&#034;, &#034;\u5206\u88c2&#034;]<br \/>\n                result &#061; &#034;\u3010\u8282\u594f\u52a8\u673a\u53d1\u5c55\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; &#034;\u539f\u59cb\u52a8\u673a: &#034; &#043; &#034; | &#034;.join([str(d) for d in motif]) &#043; &#034;\\\\n\\\\n&#034;<br \/>\n                for tech in techniques:<br \/>\n                    developed &#061; re.develop_rhythm(motif, tech)<br \/>\n                    result &#043;&#061; tech &#043; &#034;: &#034; &#043; &#034; | &#034;.join([f&#034;{d:.2f}&#034; for d in developed]) &#043; &#034;\\\\n&#034;<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(melody))]<br \/>\n                return result<\/p>\n<p>        elif technique &#061;&#061; &#034;\u5076\u7136\u97f3\u4e50&#034;:<br \/>\n            cme &#061; ChanceMusicEngine()<\/p>\n<p>            if subtype &#061;&#061; &#034;\u63b7\u9ab0\u5b50\u4f5c\u66f2&#034;:<br \/>\n                piece &#061; cme.dice_composition(n_bars&#061;min(bars, 8))<br \/>\n                result &#061; &#034;\u3010\u63b7\u9ab0\u5b50\u4f5c\u66f2&#xff08;\u83ab\u624e\u7279\u98ce\u683c&#xff09;\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; melody_to_score(piece)<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(piece))]<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u5b8c\u5168\u968f\u673a&#034;:<br \/>\n                piece &#061; cme.aleatoric_piece(n_events&#061;min(bars * 4, 50))<br \/>\n                result &#061; &#034;\u3010\u5b8c\u5168\u968f\u673a\u5076\u7136\u97f3\u4e50\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; melody_to_score([(p[0], p[1], p[2]) for p in piece])<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes([(p[0], p[1], p[2]) for p in piece]))]<br \/>\n                return result<\/p>\n<p>            elif subtype &#061;&#061; &#034;\u5143\u80de\u81ea\u52a8\u673a&#034;:<br \/>\n                piece &#061; cme.cellular_automata_music(rule&#061;30, n_generations&#061;32)<br \/>\n                result &#061; &#034;\u3010\u5143\u80de\u81ea\u52a8\u673a\u97f3\u4e50 (Rule 30)\u3011\\\\n&#034;<br \/>\n                result &#043;&#061; melody_to_score(piece)<br \/>\n                self._midi_tracks &#061; [(0, self._melody_to_midi_notes(piece))]<br \/>\n                return result<\/p>\n<p>        self._midi_tracks &#061; [(0, self._melody_to_midi_notes(melody))]<br \/>\n        return melody_to_score(melody)<\/p>\n<p>    def _render(self, text):        return melody_to_score(melody)<\/p>\n<p>    def _render(self, text):<br \/>\n        self.txt.delete(&#034;1.0&#034;, tk.END)<br \/>\n        self.txt.insert(tk.END, text)<br \/>\n        n_lines &#061; text.count(&#039;\\\\n&#039;) &#043; 1<br \/>\n        self.lbl_status.config(text&#061;f&#034;\u2713 \u5b8c\u6210 ({n_lines} \u884c)&#034;, foreground&#061;&#034;green&#034;)<br \/>\n        self.btn_generate.config(state&#061;tk.NORMAL)<\/p>\n<p>    def _do_again(self):<br \/>\n        self._do_async()<\/p>\n<p>    def _save(self):<br \/>\n        if not self._last_score:<br \/>\n            messagebox.showinfo(&#034;\u63d0\u793a&#034;, &#034;\u8bf7\u5148\u751f\u6210\u4f5c\u54c1&#034;)<br \/>\n            return<br \/>\n        path &#061; filedialog.asksaveasfilename(defaultextension&#061;&#034;.txt&#034;,<br \/>\n                                            filetypes&#061;[(&#034;\u7b80\u8c31\u6587\u4ef6&#034;, &#034;*.txt&#034;)])<br \/>\n        if path:<br \/>\n            with open(path, &#034;w&#034;, encoding&#061;&#034;utf-8&#034;) as f:<br \/>\n                f.write(self._last_score)<br \/>\n            messagebox.showinfo(&#034;\u4fdd\u5b58&#034;, f&#034;\u5df2\u4fdd\u5b58\u5230 {path}&#034;)<\/p>\n<p>    def _copy(self):<br \/>\n        if not self._last_score:<br \/>\n            return<br \/>\n        self.root.clipboard_clear()<br \/>\n        self.root.clipboard_append(self._last_score)<br \/>\n        self.lbl_status.config(text&#061;&#034;\u5df2\u590d\u5236&#034;, foreground&#061;&#034;blue&#034;)<\/p>\n<p>    def _export_midi(self):<br \/>\n        if not self._current_melody:<br \/>\n            messagebox.showinfo(&#034;\u63d0\u793a&#034;, &#034;\u8bf7\u5148\u751f\u6210\u65cb\u5f8b&#034;)<br \/>\n            return<\/p>\n<p>        path &#061; filedialog.asksaveasfilename(defaultextension&#061;&#034;.mid&#034;,<br \/>\n                                              filetypes&#061;[(&#034;MIDI\u6587\u4ef6&#034;, &#034;*.mid&#034;)])<br \/>\n        if not path:<br \/>\n            return<\/p>\n<p>        try:<br \/>\n            bpm &#061; int(self.spn_bpm.get())<br \/>\n        except:<br \/>\n            bpm &#061; 120<\/p>\n<p>        # \u4f18\u5148\u4f7f\u7528\u5df2\u4fdd\u5b58\u7684\u591a\u8f68\u6570\u636e&#xff0c;\u652f\u6301\u591a\u58f0\u90e8\u5bfc\u51fa<br \/>\n        if self._midi_tracks and len(self._midi_tracks) &gt; 0:<br \/>\n            tracks &#061; self._midi_tracks<br \/>\n        else:<br \/>\n            notes &#061; []<br \/>\n            time &#061; 0<br \/>\n            for semi, octv, dur in self._current_melody:<br \/>\n                pitch &#061; (4 &#043; octv) * 12 &#043; semi<br \/>\n                notes.append((pitch, dur, time))<br \/>\n                time &#043;&#061; dur<br \/>\n            tracks &#061; [(0, notes)]<\/p>\n<p>        success, msg &#061; export_midi(tracks, bpm, path)<br \/>\n        if success:<br \/>\n            self.lbl_status.config(text&#061;msg, foreground&#061;&#034;green&#034;)<br \/>\n            messagebox.showinfo(&#034;\u5bfc\u51fa\u6210\u529f&#034;, msg)<br \/>\n        else:<br \/>\n            self.lbl_status.config(text&#061;msg, foreground&#061;&#034;red&#034;)<br \/>\n            messagebox.showerror(&#034;\u5bfc\u51fa\u5931\u8d25&#034;, msg)<\/p>\n<p>if __name__ &#061;&#061; &#034;__main__&#034;:<br \/>\n    root &#061; tk.Tk()<br \/>\n    app &#061; CompositionApp(root)<br \/>\n    root.mainloop()<\/p>\n","protected":false},"excerpt":{"rendered":"<p># -*- coding: utf-8 -*-<br \/>\n\\&#8221;\\&#8221;\\&#8221;<br \/>\n\u4f5c\u66f2\u8f6f\u4ef6 v3 &#8211; \u6574\u5408\u7ecf\u5178\u4f5c\u66f2\u6280\u672f<br \/>\n\u652f\u6301&#xff1a;\u5bf9\u4f4d\u6cd5\u3001\u548c\u58f0\u5b66\u3001\u66f2\u5f0f\u5b66\u3001\u914d\u5668\u6cd5\u3001\u73b0\u4ee3\u6280\u6cd5<br \/>\n\u8f93\u51fa&#xff1a;\u591a\u58f0\u90e8\u7b80\u8c31  MIDI\u591a\u8f68<br \/>\n\\&#8221;\\&#8221;\\&#8221;import random<br \/>\nimport math<br \/>\nimport tkinter as tk<br \/>\nfrom tkinter import ttk, scrolledtext, messagebox, filedialog<br \/>\nfrom threading import Thre<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[81,149,427],"topic":[],"class_list":["post-85034","post","type-post","status-publish","format-standard","hentry","category-server","tag-python","tag-windows","tag-427"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.3 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.wsisp.com\/helps\/85034.html\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"og:description\" content=\"# -*- coding: utf-8 -*- &quot;&quot;&quot; \u4f5c\u66f2\u8f6f\u4ef6 v3 - \u6574\u5408\u7ecf\u5178\u4f5c\u66f2\u6280\u672f \u652f\u6301&#xff1a;\u5bf9\u4f4d\u6cd5\u3001\u548c\u58f0\u5b66\u3001\u66f2\u5f0f\u5b66\u3001\u914d\u5668\u6cd5\u3001\u73b0\u4ee3\u6280\u6cd5 \u8f93\u51fa&#xff1a;\u591a\u58f0\u90e8\u7b80\u8c31 MIDI\u591a\u8f68 &quot;&quot;&quot;import random import math import tkinter as tk from tkinter import ttk, scrolledtext, messagebox, filedialog from threading import Thre\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.wsisp.com\/helps\/85034.html\" \/>\n<meta property=\"og:site_name\" content=\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\" \/>\n<meta property=\"article:published_time\" content=\"2026-07-26T14:40:44+00:00\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"28 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/85034.html\",\"url\":\"https:\/\/www.wsisp.com\/helps\/85034.html\",\"name\":\"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"isPartOf\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\"},\"datePublished\":\"2026-07-26T14:40:44+00:00\",\"dateModified\":\"2026-07-26T14:40:44+00:00\",\"author\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.wsisp.com\/helps\/85034.html#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.wsisp.com\/helps\/85034.html\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/85034.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.wsisp.com\/helps\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#website\",\"url\":\"https:\/\/www.wsisp.com\/helps\/\",\"name\":\"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3\",\"description\":\"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"contentUrl\":\"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery\",\"caption\":\"admin\"},\"sameAs\":[\"http:\/\/wp.wsisp.com\"],\"url\":\"https:\/\/www.wsisp.com\/helps\/author\/admin\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.wsisp.com\/helps\/85034.html","og_locale":"zh_CN","og_type":"article","og_title":"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","og_description":"# -*- coding: utf-8 -*- \"\"\" \u4f5c\u66f2\u8f6f\u4ef6 v3 - \u6574\u5408\u7ecf\u5178\u4f5c\u66f2\u6280\u672f \u652f\u6301&#xff1a;\u5bf9\u4f4d\u6cd5\u3001\u548c\u58f0\u5b66\u3001\u66f2\u5f0f\u5b66\u3001\u914d\u5668\u6cd5\u3001\u73b0\u4ee3\u6280\u6cd5 \u8f93\u51fa&#xff1a;\u591a\u58f0\u90e8\u7b80\u8c31 MIDI\u591a\u8f68 \"\"\"import random import math import tkinter as tk from tkinter import ttk, scrolledtext, messagebox, filedialog from threading import Thre","og_url":"https:\/\/www.wsisp.com\/helps\/85034.html","og_site_name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","article_published_time":"2026-07-26T14:40:44+00:00","author":"admin","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"admin","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"28 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.wsisp.com\/helps\/85034.html","url":"https:\/\/www.wsisp.com\/helps\/85034.html","name":"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ - \u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","isPartOf":{"@id":"https:\/\/www.wsisp.com\/helps\/#website"},"datePublished":"2026-07-26T14:40:44+00:00","dateModified":"2026-07-26T14:40:44+00:00","author":{"@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41"},"breadcrumb":{"@id":"https:\/\/www.wsisp.com\/helps\/85034.html#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.wsisp.com\/helps\/85034.html"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.wsisp.com\/helps\/85034.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.wsisp.com\/helps"},{"@type":"ListItem","position":2,"name":"\u624b\u673a\u7aefpython\u8bed\u8a00\u4f5c\u66f2\u8f6f\u4ef6\u65b0\u7248\u4ee3\u7801QZQ"}]},{"@type":"WebSite","@id":"https:\/\/www.wsisp.com\/helps\/#website","url":"https:\/\/www.wsisp.com\/helps\/","name":"\u7f51\u7855\u4e92\u8054\u5e2e\u52a9\u4e2d\u5fc3","description":"\u9999\u6e2f\u670d\u52a1\u5668_\u9999\u6e2f\u4e91\u670d\u52a1\u5668\u8d44\u8baf_\u670d\u52a1\u5668\u5e2e\u52a9\u6587\u6863_\u670d\u52a1\u5668\u6559\u7a0b","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.wsisp.com\/helps\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/358e386c577a3ab51c4493330a20ad41","name":"admin","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.wsisp.com\/helps\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","contentUrl":"https:\/\/gravatar.wp-china-yes.net\/avatar\/?s=96&d=mystery","caption":"admin"},"sameAs":["http:\/\/wp.wsisp.com"],"url":"https:\/\/www.wsisp.com\/helps\/author\/admin"}]}},"_links":{"self":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/85034","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/comments?post=85034"}],"version-history":[{"count":0,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/posts\/85034\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/media?parent=85034"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/categories?post=85034"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/tags?post=85034"},{"taxonomy":"topic","embeddable":true,"href":"https:\/\/www.wsisp.com\/helps\/wp-json\/wp\/v2\/topic?post=85034"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}