semvec/pypi-packages huggingface dataset:
cadence-seq 0.1.0
Software sequencer
Requires Python: >=3.8

# Cadence
_[Lire en français](/README_fr.md)_

![screenshot](images/screenshot.png)

Cadence is a minimalist software music sequencer written in Python.


## Getting started

1. Install package:

```bash
$ pip install -e .
```

2. Play sequence:
```bash
$ python sequences/seq1.py
```

3. Launch visual interface:
```bash
$ cadence go
```


## Usage

Cadence consists of a Python API, a command-line interface, and a visual user interface.

### Python API usage

See `sequences/seq1.py` for an example of how to use the Cadence Python API to play a sound sequence.

### Command-line interface

After installation, run

```bash
$ cadence --help
```

to see the available CLI commands.

### Visual interface

Run the command

```bash
$ cadence go
```

in your terminal to launch the visual interface.

## Saving and loading projects

A project can be saved in two ways:
- by calling the `cadence.save_project()` function (see example at the bottom of `sequences/seq1.py`)
- via the "Save project..." button in the UI

A saved project consists of a folder called `projectname.cadence/` with the following structure:

```
projectname.cadence/
 ├─ project.json      # metadata and sound timings
 └─ sounds/           # contains all sound files
    ├─ sound1.wav
    ├─ sound2.wav
    └─ ...
```

Once saved, a project can be loaded in three ways:
- by calling the `load_project()` function
- by running the command `cadence load projectname.cadence` to launch the project in the UI
- by clicking the "Load project..." button within the UI

## Sounds

The `sounds/` directory includes a set of built-in sound files to use in your projects. You can also use your own sound files, as long as they meet the following requirements:

- Sounds must be `.wav` files
- All sounds used in a single project must have the same sample rate, bit depth, and number of channels (mono or stereo).

The `ffmpeg` command line utility can be used for converting sound files to the correct format.
bombardx 0.1.0
select_ok semantics in python
Requires Python: >=3.9

# bombard
Python bindings for rust's [select_ok](https://docs.rs/futures/latest/futures/future/fn.select_ok.html) macro.

## Motivation
Async Python currently lacks primitives for polling coroutines until the _first success_. Bombard is useful in situations where users spawn concurrent tasks which may err, but only care that at least one of them completes.

Coroutines heavily dependent on network-induced latencies (e.g. gen-ai applications which aim for fault tolerance through concurrent http retries) can benefit from `bombardx.select_ok()` to reduce wait times.

[`asyncio.wait`](https://docs.python.org/3/library/asyncio-task.html#waiting-primitives) gets close, but is unable to distinguish between an exception and a successful result as it simply returns the result of the first future completes:

```python
# some bad code
import asyncio

async def ok(i: int):
  await asyncio.sleep(i)
  print("ok")

async def err(i: int):
  await asyncio.sleep(i)
  raise RuntimeError("oops")

async def main():
    try:
        tasks = [fail(1), ok(2)]
        coroutines = map(asyncio.Task, tasks) # <- fail(1) completes before ok(2) !
        done, pending = await asyncio.wait(coroutines, return_when=asyncio.FIRST_COMPLETED)
    except RuntimeError as e:
        raise e

asyncio.run(main()) # raises a RuntimeError !
```

You could roll your own `select_ok` by repeatedly awaiting `asyncio.wait` and updating `tasks` with `pending` until you've exhausted the list, but this is not ideal.

Contrast the code above with :

```python
import asyncio
from bombardx import select_ok

async def main():
    try:
        done = await select_ok(fail(1), ok(2)) # <- select first successful
    except RuntimeError as e:
        raise e

asyncio.run(main()) # ignores runtime error and prints "ok" after 2 seconds
```

Bombard also abstracts `select_ok` through a `@bombard(num = ...)` decorator you can add to a fallible async function specifying the desired concurrency level :

```python
import random
from bombardx import bombard

async def random_sleep():
    """sleeps for a random amount of time and returns the duration"""
    t = random.randint(1, 10)
    await asyncio.sleep(t)
    return t

@bombard(num=10)
async def fallible():
  t = await random_sleep()
  if random.random() < 0.5:
    return t
  raise RuntimeError(t)
```