{"id":13783742,"url":"https://github.com/mwylde/loopers","last_synced_at":"2025-05-11T19:31:41.379Z","repository":{"id":45340610,"uuid":"182026011","full_name":"mwylde/loopers","owner":"mwylde","description":"Loopers is graphical live looper, written in Rust, designed for ease of use and rock-solid stability","archived":false,"fork":false,"pushed_at":"2023-12-11T00:07:47.000Z","size":58259,"stargazers_count":127,"open_issues_count":10,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-30T23:32:08.215Z","etag":null,"topics":["audio","jackaudio","looping","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mwylde.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-18T06:04:32.000Z","updated_at":"2024-04-21T22:00:32.000Z","dependencies_parsed_at":"2022-08-12T11:51:44.711Z","dependency_job_id":null,"html_url":"https://github.com/mwylde/loopers","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwylde%2Floopers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwylde%2Floopers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwylde%2Floopers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwylde%2Floopers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mwylde","download_url":"https://codeload.github.com/mwylde/loopers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225040407,"owners_count":17411482,"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","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","jackaudio","looping","rust"],"created_at":"2024-08-03T19:00:29.702Z","updated_at":"2024-11-17T20:31:09.896Z","avatar_url":"https://github.com/mwylde.png","language":"Rust","funding_links":[],"categories":["Standalone"],"sub_categories":[],"readme":"# Loopers\n\n![master status](https://github.com/mwylde/loopers/workflows/Rust/badge.svg?branch=master)\n[![Crate](https://img.shields.io/crates/v/loopers.svg)](https://crates.io/crates/loopers)\n\nLoopers is a graphical [live looper](http://www.livelooping.org/),\nwritten in Rust, designed for ease of use and rock-solid stability. It\ncan be used as a practice tool, compositional aid, or for performing\nlooped works in a live setting.\n\nCurrently it runs on Linux as a standalone\n[Jack](https://jackaudio.org/) application, which allows it to\ninterface with other Jack clients like effect racks, software\ninstruments, and DAWs, or on MacOS using Jack or (experimentally)\ndirectly with CoreAudio.\n\n![video of loopers](docs/demo.gif)\n\nThe system is modeled as a series of hardware loop units (like the\nBoss Loop Station) which are synchronized with a single time\ncontrol. The number of loop units is limited only by your display\nsize, and loop lengths are limited only by available memory.\n\n## Features\n\n* Multiple loops synchronized by a common time control\n* Loops can be recorded to (setting loop length), overdubbed, cleared, muted, and soloed\n* Up to four parts can be used to divide up portions of a performance\n* Supports beat, measure, and free quantization of loop commands making it easy to keep things in sync\n* Unlimited undo/redo\n* Double speed and half speed playback (and overdubbing!)\n* Every operation can be controlled via the GUI or MIDI\n* Sessions can be saved and restored\n* A built-in metronome (on a separate Jack output) helps keep you in time with your loops\n* No limitations on loop lengths aside from your computer's memory\n* Cross-fading ensures a perfect loop, every time\n* It's fun!\n\n## Getting started\n\n### Linux\n\nTo build loopers, you will need jack and sdl2.\n\nFor Ubuntu/Debian these can be installed with:\n\n```bash\n$ sudo apt install jackd2 libjack-jackd2-dev libgl1-mesa-dev libsdl2-dev\n```\n\nNow you're ready to install loopers itself. First get a rust toolchain\ninstalled (https://rustup.rs), then:\n\n```bash\n$ cargo install loopers\n```\n\nThen start it with the command\n\n```bash\n$ loopers\n```\n\n(If you get an error about Jack not running, you will need to start\nthe Jack server first. See the [Jack\ndocumentation](https://jackaudio.org/) for details.)\n\nThis will create a Jack client, which can be hooked up to your\ninputs/outputs/effects with any number of tools (I recommend\n[Claudia](https://kx.studio/Applications:Claudia) from KXStudio).\n\nLoopers should also be fully compatible with [PipeWire](https://pipewire.org/),\nwhich removes the need for a separate Jack server.\n\n### MacOS\n\nLoopers has experimental support for running on CoreAudio, the native\naudio system for MacOS (you can also run it on top of [jack](https://jackaudio.org/) on\nMacOS, which is currently better supported). To build on Mac with coreaudio:\n\n```bash\n$ brew install jack sdl2\n$ cargo install loopers\n$ loopers\n```\n\nBy default it will run using CoreAudio. To run with jack instead,\n\n```bash\n$ loopers --driver jack\n```\n\n**Note: midi is not currently supported via coreaudio, and there is no ability to choose\naudio sources and sinks (the default devices are used).**\n\n## Documentation\n\n### UI Tour\n\n![Full UI](docs/full_ui.png)\n\nThe UI is divided into two parts: the top contains the set of loopers,\nwhile the bottom contains controls and settings for the\nengine. Hovering over each looper shows controls for that looper,\nincluding setting the parts the looper is part of and controlling the\nmode.\n\nEach looper displays some key information to allow the performer to\nquickly understand its state:\n\n![Looper View](docs/looper_view.png)\n\nHovering over the looper produces controls for the looper (although\nmost performers will prefer to use hardware buttons)\n\n![Looper Controls](docs/looper_control_view.png)\n\nAt the bottom we find the engine controls\n\n![Engine Controls](docs/engine_controls.png)\n\n### Looper Modes\n\nAt any given time each looper can be in one of several modes, each\nidentified in the UI by a color:\n\n\u003cdl\u003e\n\u003cdt\u003e\u003cimg src=\"docs/play_color.png\" alt=\"play color\"\u003e Play\u003c/dt\u003e\n\u003cdd\u003e\nIn play mode, the output from the looper is mixed in with all of the other\nloopers and sent to the global output. This is the default mode.\n\u003c/dd\u003e\n\n\u003cdt\u003e\u003cimg src=\"docs/record_color.png\" alt=\"record color\"\u003e Record\u003c/dt\u003e\n\u003cdd\u003e\nWhen recording is started, all existing samples for the looper are cleared.\nThen new audio is recorded to a sample until recording is finished, establishing\nthe loop length.\n\u003c/dd\u003e\n\n\u003cdt\u003e\u003cimg src=\"docs/overdub_color.png\" alt=\"overdub color\"\u003e Overdub\u003c/dt\u003e\n\u003cdd\u003e\nIn overdub mode, we add new input on top of the existing samples in the looper,\nwithout changing the loop length.\n\u003c/dd\u003e\n\n\u003c/dl\u003e\n\nIn addition to those exclusive modes, a looper can have one or more of\nthe following _modifiers_:\n\n\n\u003cdl\u003e\n\u003cdt\u003e\u003cimg src=\"docs/play_color.png\" alt=\"solo color\"\u003e Solo\u003c/dt\u003e\n\u003cdd\u003e\nIf solo is enabled, all other loopers will be silenced (aside from those that are\nalso in solo mode).\n\u003c/dd\u003e\n\n\u003cdt\u003e\u003cimg src=\"docs/mute_color.png\" alt=\"mute color\"\u003e Mute\u003c/dt\u003e\n\u003cdd\u003e\nWhen mute is enabled the looper is silenced.\n\u003c/dd\u003e\n\u003c/dl\u003e\n\nThe modes and modifiers can be controlled via the UI or by sending a\nmidi command.\n\n### Quantization\n\nWhen using multiple loopers, it is generally desirable for them to be\nsynchronized together. In Loopers, this is accomplished by having a\nsingle time control which is used across all loopers. It is also key\nthat loops have lengths which are exact multiples of each other (for\nexample, you might have a bass loop that is 4 times as long as your\nrhythm loop). If the length is off by even a few milliseconds, it will\nquickly be noticeable after a few repeats.\n\nTo help performers get loop lengths exactly in sync, Loopers provide a\n_quantization_ feature. This allows you to synchronize commands (for\nexample, stopping recording and thus setting loop length) to certain\nmetric events.\n\nIt supports three quantization modes, set via buttons at the bottom of\nthe UI:\n\n* **Free** this disables quantization, and causes all commands to take\n  effect immediately\n* **Beat** commands take effect on the next beat after they are\n  issued, as determined by the tempo\n* **Measure** commands take effect at the start of the next measure,\n  as determined by the tempo and time signature\n\nSome commands are affected by quantization, and some take effect\nimmediately. See the [commands reference](#commands) for more.\n\n### Commands\n\nEvery aspect of the system can be controlled via commands, both in the\nUI and via midi. For details on configuring midi controls, see\n[settings](#settings).\n\nThere are two kinds of commands: looper commands, which are applied to\none or more loopers, and engine commands which apply to the system as\na whole. Commands can take parameters which control the behavior of the\ncommand.\n\nLooper commands all take a first parameter that determines\nwhich looper will be targeted:\n\n* **Selected**: targets only the selected looper, as controlled via\n  the UI or by one of the `Select*Looper` commands\n* **Id**: takes an _id_ parameter, and targets the looper with that\n  id\n* **Index**: takes an _index_ parameter and targets the looper at that\n  index in the currently visible part, starting from 0.\n* **All**: targets all loopers\n\nOther commands may also take parameters which control their\nbehavior.\n\nCommands also differ in how they are affected by quantization:\n\n* **Immediate** commands take place as soon as they are received,\n  regardless of quantization settings or the state of the system\n* **Queued** commands will wait in a queue for other (possibly\n  quantized) commands to take effect before being executed. This\n  allows you to, for example, send the command to switch to the next\n  part (a quantized command), then send a SelectNextLooper command\n  (a queued command), which will wait for part switch before\n  executing.\n* **Quantized** commands will wait until the next quantization\n  boundary (e.g., the start of the next measure, see\n  [Quantization](#Quantization) for details) to execute.\n\n#### Looper commands\n\n| **Command** | **Parameters** | **Quantization** | **Description** |\n|-|-|-|-|\n| Record | Looper Targets | Quantized | Moves the selected loopers to the Record mode |\n| Overdub | Looper Targets | Quantized | Moves the selected loopers to the Overdub mode |\n| Play | Looper Targets | Quantized | Moves the selected loopers to the Play mode |\n| RecordOverdubPlay | Looper Targets | Quantized① | Cycles from Record -\u003e Overdub -\u003e Play -\u003e Overdub |\n| Mute | Looper Targets | Immediate | Toggles the mute modifier on the selected loopers |\n| Solo | Looper Targets | Immediate | Toggles the solo modifier on the selected loopers |\n| Delete | Looper Targets | Immediate | Deletes the selected loopers |\n| Clear | Looper Targets | Quantized | Clears all samples from the selected loopers |\n| SetPan | Looper Targets, a pan value from -1 (fully left) to 1 (fully right) | Immediate | Sets the pan for the looper |\n| SetLevel | Looper Targets, a level value from 0 (silent) to 1 (full volume) | Immediate | Sets the output level for the looper |\n| 1/2x | Looper Targets | Immediate | Sets the looper to 1/2x speed |\n| 1x | Looper Targets | Immediate | Sets the looper to 1x speed |\n| 2x | Looper Targets | Immediate | Sets the looper to 2x speed |\n\n① _RecordOverdubPlay is quantized from Record -\u003e Overdub and Overdub -\u003e\nPlay, but queued from Play -\u003e Overdub._\n\n#### Engine commands\n\n| **Command** | **Parameters** | **Quantization** | **Description** |\n|-|-|-|-|\n| Start | _None_ | Immediate | Starts the engine |\n| Stop | _None_ | Immediate | Stops the engine, resetting the time |\n| StartStop | _None_ | Immediate | Starts the engine if it is stopped, otherwise stops it |\n| PlayPause | _None_ | Immediate | Pauses the engine if it is active, otherwise restarts it |\n| Pause | _None_ | Immediate | Stops the engine but does not reset the time |\n| Reset | _None_ | Immediate | Resets the engine time |\n| SetTime | Time (in samples) | Immediate | Sets the time to the specified number of samples |\n| AddLooper | _None_ | Immediate | Adds a looper to the end of the current part |\n| SelectLooperById | Looper Id | Immediate | Selects the looper with the given id |\n| SelectLooperByIndex | Index | Immediate | Selects the looper at the given index in the current part (starting from 0) |\n| SelectPreviousLooper | _None_ | Queued | Selects the previous looper in the current part, wrapping around from the first to the last |\n| SelectNextLooper | _None_ | Queued | Selects the next looper in the current part, wrapping around from the last to the first |\n| PreviousPart | _None_ | Quantized | Goes to the previous part, skipping those parts with no loopers |\n| NextPart | _None_ | Quantized | Goes to the next part, skipping those parts with no loopers |\n| GoToPart | One of `A`, `B`, `C`, or `D` | Quantized | Goes to the specified part |\n| SetQuantizationMode | One of `Free`, `Beat`, or `Measure` | Immediate | Sets the quantization mode for the engine |\n| SetMetronomeLevel | 0-100 | Immediate | Sets the metronome volume to the given percentage |\n| SetTempoBPM | bpm (float) | Immediate | Sets the engine's tempo to the given BPM value |\n| SetTimeSignature | upper, lower | Immediate | Sets the engine's time signature according to the parameters (e.g. 3, 4) |\n| SaveSession | Path | Immediate | Saves the current session to the given path |\n| LoadSession | Path | Immediate | Loads a session from the given path, replacing the existing one |\n\n\n### Settings\n\nConfiguration is stored the standard system user config location\n(typically this will be ~/.config/loopers/). Currently the configuration\nconsists of a set of mappings from midi messages to loopers\ncommands. These should be placed in a file called `midi_mappings.tsv`\nin that config directory, which will be automatically created after loopers\nis run for the first time.\n\nEach non-empty line of this file should contain the following\ntab-separated columns:\n\n1. Midi channel (either `*` for any channel or a channel number)\n2. Midi controller number\n3. Midi data (can be `*` for any data, a single value like `50`,\n   or a range like `0-100`)\n4. Command name (see tables above)\n5. Command arguments (multiple arguments should be tab-separated; the special \n   value `$data` can be used in the place of certain numerical arguments to \n   use the data value of the midi event, for example for use with an expression \n   pedal)\n   \nThe midi values (channel, controller, data) can be thought of as _filters_ for incoming midi events; for each event\nall matching commands will fire.\n\nAn example for configuring for use with the [Behringer\nFCB1010](https://www.behringer.com/product.html?modelCode=P0089) (an\nexcellent pedalboard):\n\n``` tsv\nChannel\tController\tData\tCommand\tArg1\tArg2\tArg3\n*\t22\t127\tRecordOverdubPlay\tSelected\n*\t22\t0\tRecordOverdubPlay\tSelected\n\n*\t23\t127\tSelectNextLooper\n*\t23\t0\tSelectNextLooper\n\n*\t24\t127\tNextPart\n*\t24\t0\tNextPart\n\n*\t25\t127\tClear\tSelected\n*\t25\t0\tClear\tSelected\n\n*\t26\t127\tPlayPause\n*\t26\t0\tPlayPause\n\n*\t27\t0-127\tSetPan\tSelected\t$data\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwylde%2Floopers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmwylde%2Floopers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwylde%2Floopers/lists"}