{"id":16733201,"url":"https://github.com/krasjet/usporth","last_synced_at":"2025-07-22T04:32:00.420Z","repository":{"id":98566648,"uuid":"415546923","full_name":"Krasjet/usporth","owner":"Krasjet","description":"a minimal rewrite of sporth core, a small stack-based audio programming language","archived":false,"fork":false,"pushed_at":"2021-12-26T08:15:41.000Z","size":173,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-15T20:14:39.259Z","etag":null,"topics":["audio","c","forth","programming-language"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Krasjet.png","metadata":{"files":{"readme":"README","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2021-10-10T10:00:47.000Z","updated_at":"2021-12-26T08:15:43.000Z","dependencies_parsed_at":"2023-07-13T08:30:10.768Z","dependency_job_id":null,"html_url":"https://github.com/Krasjet/usporth","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Krasjet/usporth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krasjet%2Fusporth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krasjet%2Fusporth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krasjet%2Fusporth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krasjet%2Fusporth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Krasjet","download_url":"https://codeload.github.com/Krasjet/usporth/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Krasjet%2Fusporth/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266427866,"owners_count":23926890,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","c","forth","programming-language"],"created_at":"2024-10-12T23:49:00.392Z","updated_at":"2025-07-22T04:32:00.392Z","avatar_url":"https://github.com/Krasjet.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"μsporth\n=======\n\nA minimal rewrite of the core of Paul Batchelor's Sporth [1],\na small stack-based audio programming language.\n\nThe main goal of μsporth is to create a self-contained audio\nprogramming language that is actually possible to extend and\ncomprehend completely within one or two hours, given some C\nprogramming background.\n\nThe stack-based approach of Sporth is great for this goal,\nbut the original implementation is a bit convoluted and it\nis mainly a wrapper of Soundpipe for personal use. It's not\nstraightforward to make it your own. Though luckily, the\nconcept of Sporth is so simple that many parts can be\ngreatly simplified if you rethink it from scratch.\n\nμsporth implements most of the core features of Sporth in\naround 500 lines of C (`usporth.c` + `usporth.h`). It is\nprobably even more extensible since the DSP part is now\ncompletely independent of the IO part. This is why multiple\ninterfaces to μsporth could be implemented as independent\nstandalone programs:\n\n  usporth_text: text interface, mainly for debugging\n  usporth_jack: real-time audio interface via JACK\n  usporth_wav:  offline rendering interface via libsndfile\n\nμsporth doesn't include all the unit generators in Sporth.\nExisting ugens are mostly to help developing and testing new\nugens and to demonstrate how you can extend and use the\nlanguage. You can treat it as a \"bring your own ugens\"\nversion of Sporth, a blank slate for you to implement your\nown language, also a great playground to experiment with DSP\nalgorithms in C.\n\nHow to read\n-----------\n\nTo understand the implementation of μsporth, I suggest you\nstart with \"The Stack\" section of the Sporth Cookbook\n\n  https://web.archive.org/web/20211015023825/https://pbat.ch/proj/cook/stack.html\n\nNotice that the `sine` ugen of μsporth doesn't include the\namplitude parameter, so\n\n     Sporth           μsporth\n  440 0.5 sine \u003c=\u003e 440 sine 0.5 *\n\nSyntax-wise, μsporth is equivalent to Sporth, possibly\nslightly more flexible since it allows numbers without\nleading zeros. There are only three types of elements in\nμsporth source code\n\n  # numbers (floats)\n  1.23 -1.23 .23\n  # strings\n  _word \"is equivalent to\" 'string without spaces'\n  # ugens\n  sine + - * /\n\nEach element is compiled into a \"pipe\",\n\n  usp_pipe pipes = usporth_eval(\"440 sine 0.5 *\");\n\n     440 sine 0.5 *\n  =\u003e\n     +f----+  +u-----+  +f----+  +u--+\n     | 440 +=\u003e| sine +=\u003e| 0.5 +=\u003e| * +=\u003e\n     +-----+  +------+  +-----+  +---+\n\nwhich is an instance of the element, with its own internal\nstates, that can pump out and comsume values on the stack in\na sequential, streaming fashion. We call a sequence of pipes\na \"pipeline\".\n\nGiven a pipeline, we can evaluate all the pipes in the\npipeline sequentially using\n\n  pipes_init(\u0026ctx, pipes); /* init all pipes */\n  pipes_tick(\u0026ctx, pipes); /* compute a sample */\n  pipes_free(pipes);       /* clean up all pipes */\n\nwhere `ctx` is the runtime context of μsporth, which holds\nthe data that need to be shared among all ugens, such as the\nstack and the sampling rate. It can be initialized using\n\n  usp_ctx ctx;\n  usporth_init_ctx(\u0026ctx, sr);\n\nTo get a full picture of the μsporth API, `main_text.c` is a\ngreat place to start. Then follow the implementation of\nthese five functions to understand the rest of the\nimplementation.\n\nAdding a new ugen\n-----------------\n\nTo add a new unit generator, e.g. inv (this one is already\nimplemented now),\n\n1. Create `inv.c` in `ugens` directory.\n\n2. Define `init`, `tick`, and `free` functions for the new\n   ugen. The name and prototype of the functions must match\n   the following pattern:\n\n     ugen_status ugen_inv_init(usp_ctx *ctx, ugen_instance *pugen)\n     ugen_status ugen_inv_tick(usp_ctx *ctx, ugen_instance ugen)\n     void ugen_inv_free(ugen_instance ugen)\n\n   `ugen_instance` is how the ugen keeps an internal state.\n   It is similar to LADSPA's `LADSPA_Handle` [2] or LV2's\n   `LV2_Handle` [3].\n\n   If the ugen needs an internal state, you need to store it\n   in `pugen` during the init phase. Then the state will be\n   available to you via the `ugen` argument during the tick\n   and free phase. See existing implementations in `ugens`\n   directory for some examples. I recommend starting with\n   `sine.c`.\n\n   I also recommend checking out `ftgen.c`. It shows you how\n   to use \"extension data\", which is how custom data can be\n   shared between different ugens. It also demonstrate two\n   important quirks:\n\n     1. String arguments are only pushed onto the stack in\n        init phase, not in tick phase.\n     2. In the `free` function, you must make sure the\n        program doesn't crash when `ugen` is NULL.\n\n3. Add documentation to `ugens.lua`, e.g.\n\n     ['inv'] = {\n       input = {\n         {name = 'v', type = 'f', cond = 'x != 0'},\n       },\n       output = {\n         {name = 'inv', type = 'f'},\n       },\n       description = 'compute 1 / v'\n     },\n\n   to the `ugens` table.\n\n4. Run\n\n     $ lua ./ugens.lua\n\n   to regenerate `ugens.h`. You need to have lua installed\n   for this.\n\n5. Update the makefile to include the new object file\n   `ugens/inv.o` to `OBJ` and add header dependency\n\n     ugens/inv.o: usporth.h\n\n   This step might be automated in the future.\n\nUsage\n-----\n\nJACK interface:\n\n  $ usporth_jack in.usp\n\nText interface:\n\n  $ usporth_text [-s] [-n nsamples] [-r sr] in.usp\n  $ usporth_text [-s] [-n nsamples] [-r sr] in.usp \u003c in.txt\n\n  Examples:\n\n    $ usporth_text -s -n 20 -r 48000 examples/sine.usp\n    $ echo '1 2 3' | usporth_text -n 3 examples/in.usp\n\n  The output can be directly piped into gnuplot using\n\n    $ gnuplot -p -e \"plot '\u003c cat -' w lines\"\n\nWAV interface:\n\n  $ usporth_wav [-t nsecs] [-r sr] [-o out.wav] in.usp\n  $ usporth_wav [-t nsecs] -i in.wav [-o out.wav] in.usp\n\n  Examples:\n\n    $ usporth_wav -t 3 -r 8000 -o sine.wav examples/sine.usp\n    $ usporth_wav -i sine.wav -o sine_out.wav examples/in.usp\n\nSee the man pages for details.\n\nBuild\n-----\n\nTo build the JACK interface, you need JACK [4] installed on\nyour system, and to build the WAV interface, you need\nlibsndfile [5] installed. If you want to add new ugens, you\nalso need to have lua installed.\n\nThe text interface only requires a C99-compatible C compiler\nto build.\n\nFor Arch-based distros, you can install all the optional\ndependencies by\n\n  $ pacman -S jack2 libsndfile lua\n\nFor Debian-based distros, install by\n\n  $ apt-get install libjack-jackd2-dev libsndfile1-dev lua5.1\n\nFor FreeBSD, install by\n\n  $ pkg install pkgconf audio/jack libsndfile lua51\n\nFor macOS, you can use Homebrew [6]:\n\n  $ brew install pkgconf jack libsndfile lua\n\nFor Windows, you can use MSYS2 with MinGW [7]:\n\n  $ pacman -S pkg-config mingw-w64-x86_64-libsndfile mingw-w64-x86_64-lua\n\n  If you want to compile the JACK interface, you need to\n  build JACK from source:\n\n    $ pacman -S git base-devel python mingw-w64-x86_64-toolchain\n    $ git clone git://github.com/jackaudio/jack2.git\n    $ cd jack2\n    $ ./waf configure \\\n          --prefix=${MINGW_PREFIX} \\\n          --check-c-compiler=gcc \\\n          --check-cxx-compiler=g++\n    $ ./waf build\n    $ ./waf install\n\nAfter you have sorted out the dependencies, use\n\n  $ ./configure\n\nto generate a `config.mk` file containing compiler flags,\nand run\n\n  $ make\n\nto build the programs. If you want to install μsporth to\nyour system, use\n\n  $ make install\n\nor you can specify a destination with\n\n  $ make PREFIX=~/.local install\n\nRun\n\n  $ make uninstall\n\nto uninstall μsporth.\n\n[1]: https://web.archive.org/web/20211117190245/https://pbat.ch/proj/sporth.html\n[2]: https://www.ladspa.org/\n[3]: https://lv2plug.in/\n[4]: https://jackaudio.org/\n[5]: https://libsndfile.github.io/libsndfile/\n[6]: https://brew.sh/\n[7]: https://www.msys2.org/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasjet%2Fusporth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrasjet%2Fusporth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasjet%2Fusporth/lists"}