{"id":44904880,"url":"https://github.com/christofmuc/pytschirp","last_synced_at":"2026-02-17T22:14:01.276Z","repository":{"id":45185793,"uuid":"225733112","full_name":"christofmuc/pytschirp","owner":"christofmuc","description":"MIDI synthesizer and synthesizer patch bindings for python - this is synth programming for real!","archived":false,"fork":false,"pushed_at":"2025-03-08T16:24:32.000Z","size":74,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-08T17:25:33.112Z","etag":null,"topics":["midi","midi-api","prophet","python3","synthesizer"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/christofmuc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-03T22:54:46.000Z","updated_at":"2025-03-08T16:24:36.000Z","dependencies_parsed_at":"2024-07-07T17:41:23.235Z","dependency_job_id":"5ae8509a-510c-42ff-bb27-ffef7548bb16","html_url":"https://github.com/christofmuc/pytschirp","commit_stats":null,"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"purl":"pkg:github/christofmuc/pytschirp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christofmuc%2Fpytschirp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christofmuc%2Fpytschirp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christofmuc%2Fpytschirp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christofmuc%2Fpytschirp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/christofmuc","download_url":"https://codeload.github.com/christofmuc/pytschirp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christofmuc%2Fpytschirp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29560019,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T21:50:49.831Z","status":"ssl_error","status_checked_at":"2026-02-17T21:46:15.313Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["midi","midi-api","prophet","python3","synthesizer"],"created_at":"2026-02-17T22:14:00.796Z","updated_at":"2026-02-17T22:14:01.264Z","avatar_url":"https://github.com/christofmuc.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\nThis project builds Python language bindings for MIDI-capable synthesizers, to allow you to program these synthesizers in, you guessed it, Python. \n\n*Real synthesizer programming.*\n\nFor the time being, currently only the [Sequential](https://www.sequential.com/) Prophet Rev2 synthesizer is supported, but many more synths are on their way, let us know if you have a specific device you want supported.\n\n# Installing\n\nThe easiest way to test the functionality is to head over to the little UI program we made called [PyTschirper](https://github.com/christofmuc/PyTschirper) here on github. When you install that (setup available for Windows), you get an experimental IDE so you can try it out.\n\nThe PyTschirper setup also installs a Windows Python binary module that you can import, see below \"Usage\" for details.\n\n## Building\n\nThis is a git submodule that is intended to be used within a larger environment, therefore we don't provide build instructions here. To check out how it works, look at the [PyTschirper](https://github.com/christofmuc/PyTschirper) software here on github.\n\n# Usage\n\nAs usual, you will need to import the pytschirp module into your python code before being able to use it:\n\n    import pytschirp\n\nThe module itself is using native code, so on Windows you will need to point the sys.path to the folder where the file `pytschirp.cp36-win_amd64.pyd` is located. This is the name for a specific platform build (AMD64) for a specific python version (3.6). \n\nThe pytschirp module provides three different main classes that can be used to manipulate synthesizers:\n\n  1. A `Synthesizer` class representing the MIDI synth that you want to acesss. Note that this is a high level implementation of a specific synth like the Sequential DSI Prophet Rev 2, not a low level implementation where you have to deal with MIDI bytes, hexdumps, and sysex codes.\n  2. A `Patch` class that represents one single patch (tone/program/...) of the synthesizer.\n  3. A `PatchAttribute` class that represents one of the many parameters of a concrete Patch, and this PatchAttribute can be used to retrieve the value or modify a Patch. If the Patch was retrieved from a live device, the modification of the PatchAtribute at the same time changes the attribute on the live device (via MIDI).\n\n## Synthesizer class\n\nThis is the first class that you will need to instantiate. For now, the only synth supported is the Prophet Rev2, so we can simply create an object that represents the synth:\n\n    r = pytschirp.Rev2()\n\n### Loading and saving sysex files\n\nThe synth class can be used to load and save sysex files in the native format for that synthesizer, e.g. to load the 512 factory patches of the Rev2 into a python array, just do (given the syx file is in the current working directory)\n\n    factory_patches = r.loadSysex('Rev2_Programs_v1.0.syx')\n\nYou can also resave these patches into a new file, e.g. if you did some modification to them using the synthesizer object:\n\n    r.saveSysex('modifed.syx', factory_patches)\n\nThis will produce a bank dump sysex file. To save only a single patch as an edit buffer dump (which will not overwrite any of the synth's storage places when sent to the synth):\n\n    r.saveEditBuffer('editBuffer_dump_1.syx', factory_patches[12])\n\n### Detecting the live device \n\nUntil now, we were manipulating data structures of the Rev2 without needing any access to a physical device, but of course with the real thing, the fun only starts. If you have connected your Rev2 to your computer in a bidirectional MIDI connection (or just with USB), you can run\n\n    r.detect()\n\nwhich will take a few seconds depending on the number of MIDI ports on your computer, and a check can confirm that we are now talking to the synth:\n\n    print(r.detected())  # Should give True\n\nWe can also check the MIDI interface/port that is used, in some scenarios this is important to know to debug:\n\n    print(r.location())  # E.g. MIDI IN: Port 1 on MXPXT, MIDI OUT: Port 1 on MXPXT\n\n### Doing live editing\n\nThere is a special type of patch that you retrieve from a successfully detected synthesizer object, and that is the current edit buffer. The edit buffer patch is different than patches retrieved e.g. via the loadSysex() command because any modification made to the editbuffer object will immediately cause MIDI messages to be generated and sent to the synth that make that modifcation also in the live device.\n\nTo retrieve the edit buffer patch, just do\n\n    e = r.editBuffer()\n\n## Patch class\n\nTo create an init patch for the Rev2, just create the object with\n\n    p = pytschirp.Rev2Patch()\n\nThe patch itself is a collection of parameter values that determine the setup of the synthesizer for that particular sound. So the most important question to the patch is the list of parameters it has:\n\n    print(p.parameterNames())\n\nDon't be surpised if you get a list of more than one hundred parameters, the Rev2 is a pretty complex machine (154 to be precise, check with `len(p.parameterNames())`). \n\n### Attribute access\n\nOnce you know the name of an attribute you're interested in, you can get the attribute accessor object either via the attr() function, or just pretend the patch itself is a dictionary with the parameterNames as keys:\n\n    cutoff_attribute = p.attr('Cutoff')\n    cutoff_attribute = p['Cutoff']\n\n### Patch name\n\nThe patch name currently is read only (that's on the list of improvements), and can be accessed via it's own attribute:\n\n    print(p.name)\n\n### Layers\n\nAs the Rev2 is actually dual layered, with both Layers having exactly the same parameters, you can retrieve the Layer A and Layer B via a method of the patch - and the returned object is in turn again a Patch, but all accessors to the parameters access layer A or B respectively:\n\n    a = p.layer(0)\n    b = p.layer(1)\n\n## PatchAttribute class\n\nThe PatchAttribute class is your invisible helper in modifying the values of a patch. You will not need to instantiate any of these, or store objects of this type. They are used while interacting with the Patch class.\n\nMost importantly, we support four different types of attributes:\n\n    1. Single value\n    2. Vector value\n    3. Lookup value\n    4. Vector of Lookup Values \n\n### Single Value\n\nThe single value attribute is the most basic one, and doesn't have any surprises. It feels like an Integer in python, and this is also how you can use it. The range is defined by the synth implementation. E.g. do\n\n    cutoff_value = p[\"Cutoff\"]  # gives the integer value of the cutoff    \n    print(cutoff_value)  \n    p[\"Cutoff\"] = 128  # Allows direct setting of the integer value\n    print(p[\"Cutoff\"])  # Shows new value in patch\n\n### Vector Value\n\nThis behaves like an integer vector in Python, and us used e.g. for the gated sequencer tracks, each track being a vector of length 16:\n\n    print(p['Seq Track 1'])  # Produces e.g. [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n    p['Seq Track 1'] = [1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1]  # Numbers\n    print(p['Seq Track 1'])\n    import random\n    p['Seq Track 1'] = [random.randrange(30, 80) for _ in range(16)]  # Randomize\n    print(p['Seq Track 1'])\n\n### Lookup Value \n\nMany parameters act more like enums than as continuous values, we call those Lookup Values because there is effectively a lookup table or function to produce a descriptive string from the stored number. This is used automatically when printing a value:\n\n    print(p['Gated Seq Mode'])  # Prints e.g. 'Normal'\n\nYou can retrieve the integer value again via the get() function \n\n    print(p['Gated Seq Mode'].get())  # Prints e.g. '0'\n\nSetting currently doesn't support the reverse lookup, but needs to integer number:\n\n    p['Gated Seq Mode'] = 2\n\n\n### Vector of Lookup Values\n\nNothing special here, these can be treated exactly like the vector valued attributes, the only advantage is that they will produce a nicer display via the lookup table when printed. E.g. you get the note names displayed when you print out the note track from the poly sequencer:\n\n    print(p['Poly Seq Note 1'])  # would give something like ['D#3', 'A#4', 'D#5', ...]\n    print(p['Poly Seq Note 1'].get())  # would give something like [60, 64, 255, 255, 60, ...]\n\n## Licensing\n\nAs some substantial work has gone into the development of this, I decided to offer a dual license - AGPL, see the LICENSE.md file for the details, for everybody interested in how this works and willing to spend some time her- or himself on this, and a commercial MIT license available from me on request. Thus I can help the OpenSource community without blocking possible commercial applications.\n\n## Contributing\n\nAll pull requests and issues welcome, I will try to get back to you as soon as I can. Due to the dual licensing please be aware that I will need to request transfer of copyright on accepting a PR. \n\n## About the author\n\nChristof is a lifelong software developer having worked in various industries, and can't stop his programming hobby anyway. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristofmuc%2Fpytschirp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchristofmuc%2Fpytschirp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristofmuc%2Fpytschirp/lists"}