diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a95067 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +venv/ +__pycache__ +.env \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0ceb6db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} diff --git a/config.py b/config.py new file mode 100644 index 0000000..7689f54 --- /dev/null +++ b/config.py @@ -0,0 +1,6 @@ +import os + +class Config(object): + DEBUG = False + FLASK_APP = os.environ.get('FLASK_APP') + FLASK_ENV = os.environ.get('FLASK_ENV') \ No newline at end of file diff --git a/clients.jpg b/img/clients.jpg similarity index 100% rename from clients.jpg rename to img/clients.jpg diff --git a/cover.jpg b/img/cover.jpg similarity index 100% rename from cover.jpg rename to img/cover.jpg diff --git a/spaghetti.jpg b/img/spaghetti.jpg similarity index 100% rename from spaghetti.jpg rename to img/spaghetti.jpg diff --git a/structure.jpg b/img/structure.jpg similarity index 100% rename from structure.jpg rename to img/structure.jpg diff --git a/readme.md b/readme.md index eb17137..96f0ebd 100644 --- a/readme.md +++ b/readme.md @@ -1,17 +1,17 @@ # Skimmer: multi-channel & displaced muzik -![cover with the skimmer](cover.jpg) +![cover with the skimmer](img/cover.jpg) The Skimmer is a container for experiments on multi-channel and displaced sound works, developed in the context of SI18. The main concept is a system that streams to various connected clients, using them as speakers. Unlike classical broadcast transmissions where everyone receive the same signal, here each client is an individual channel. In doing so, the Skimmer can have as many channels as many clients are connected. This opens interesting spatial and expressive possibilities that I would like to explore. To rely on connected clients and the public as a founding part of the instrument raises questions about instability and contingency in both composition and design. What does it mean for the public to host the instrument, and what does it mean for the performer to be hosted by the public? Which kind of politics and relations are generated? The Skimmer works with a lightweight setup: a server application links together a source and the connected clients. Instead of streaming the audio directly from the source to the server, what is shared is a model to generate the sounds. With this approach the stream consists in just messages for modulating the instrument on each client, and the traffic it is super light. This require a sound design oriented to the specs of the clients. -![skimming the notes](clients.jpg) +![skimming the notes](img/clients.jpg) ## Structure -![structure with a pot](structure.jpg) +![structure with a pot](img/structure.jpg) This setup is made by three main parts: @@ -30,7 +30,7 @@ The source is the instrument that the server and clients share. The recipe of th - The handle will be maybe something similar to the [modular spaghetti interface](https://git.xpub.nl/kamo/spaghetti)? (not sure yet, but i like the idea of drawing connections between the source and the clients) - The sift will be a little server in the Soupboat, not sure yet if with node.js and express (pro: all JS pipeline) or flask (pro: mixed pipeline and familiar pattern) -![spaghetti setup](spaghetti.jpg) +![spaghetti setup](img/spaghetti.jpg) Prototype of the spaghetti cables interface ### Process diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f12a61d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,22 @@ +bidict==0.22.0 +black==22.3.0 +click==8.1.2 +colorama==0.4.4 +dnspython==2.2.1 +eventlet==0.33.0 +Flask==2.1.1 +Flask-SocketIO==5.1.1 +greenlet==1.1.2 +itsdangerous==2.1.2 +Jinja2==3.1.1 +Mako==1.2.0 +MarkupSafe==2.1.1 +mypy-extensions==0.4.3 +pathspec==0.9.0 +platformdirs==2.5.1 +python-dotenv==0.20.0 +python-engineio==4.3.1 +python-socketio==5.5.2 +six==1.16.0 +tomli==2.0.1 +Werkzeug==2.1.1 diff --git a/index.html b/sequencer/index.html similarity index 100% rename from index.html rename to sequencer/index.html diff --git a/index.js b/sequencer/index.js similarity index 100% rename from index.js rename to sequencer/index.js diff --git a/style.css b/sequencer/style.css similarity index 100% rename from style.css rename to sequencer/style.css diff --git a/skimmer/__init__.py b/skimmer/__init__.py new file mode 100644 index 0000000..ce1a0fe --- /dev/null +++ b/skimmer/__init__.py @@ -0,0 +1,47 @@ +import os +from flask import Flask, send_from_directory +from flask_socketio import SocketIO +from . import prefix + +socketio = SocketIO() + + +def create_app(test_config=None): + # Create and configure the Flask App + app = Flask(__name__, instance_relative_config=True) + app.config.from_mapping( + SECRET_KEY="dev", + ) + + if test_config is None: + # load the instance config, if it exists, when not testing + app.config.from_pyfile("config.py", silent=True) + else: + # load the test config if passed in + app.config.from_mapping(test_config) + + # ensure the instance folder exists + try: + os.makedirs(app.instance_path) + except OSError: + pass + + @app.route("/favicon.ico") + def favicon(): + return send_from_directory( + os.path.join(app.root_path, "static"), + "favicon.ico", + mimetype="image/vnd.microsoft.icon", + ) + + from . import bowl + + app.register_blueprint(bowl.bp) + + app.wsgi_app = prefix.PrefixMiddleware( + app.wsgi_app, prefix=os.environ.get("URL_PREFIX", "") + ) + + socketio.init_app(app) + + return app diff --git a/skimmer/bowl.py b/skimmer/bowl.py new file mode 100644 index 0000000..e1974e0 --- /dev/null +++ b/skimmer/bowl.py @@ -0,0 +1,11 @@ +from flask import Blueprint +from mako.template import Template + +from . import events + +bp = Blueprint("bowl", __name__) + + +@bp.route("/") +def bowl(): + return Template(filename="skimmer/templates/bowl.html").render() diff --git a/skimmer/events.py b/skimmer/events.py new file mode 100644 index 0000000..ac0b9dd --- /dev/null +++ b/skimmer/events.py @@ -0,0 +1,10 @@ +from flask import session +from flask_socketio import emit + +from . import socketio + + +@socketio.on("my event") +def test(message): + print(message) + emit("message", {"msg": "eheh"}) diff --git a/skimmer/prefix.py b/skimmer/prefix.py new file mode 100644 index 0000000..3d6a9cc --- /dev/null +++ b/skimmer/prefix.py @@ -0,0 +1,14 @@ +class PrefixMiddleware(object): + def __init__(self, app, prefix=""): + self.app = app + self.prefix = prefix + + def __call__(self, environ, start_response): + + if environ["PATH_INFO"].startswith(self.prefix): + environ["PATH_INFO"] = environ["PATH_INFO"][len(self.prefix) :] + environ["SCRIPT_NAME"] = self.prefix + return self.app(environ, start_response) + else: + start_response("404", [("Content-Type", "text/plain")]) + return ["This url does not belong to the app.".encode()] \ No newline at end of file diff --git a/skimmer/static/favicon.ico b/skimmer/static/favicon.ico new file mode 100644 index 0000000..1663939 Binary files /dev/null and b/skimmer/static/favicon.ico differ diff --git a/skimmer/templates/bowl.html b/skimmer/templates/bowl.html new file mode 100644 index 0000000..4944f1f --- /dev/null +++ b/skimmer/templates/bowl.html @@ -0,0 +1,24 @@ + + + + + + + Document + + + + + + Hello Bonjur + + diff --git a/skimmer/templates/pot.html b/skimmer/templates/pot.html new file mode 100644 index 0000000..dd58b3f --- /dev/null +++ b/skimmer/templates/pot.html @@ -0,0 +1,24 @@ + + + + + + + 🍲 Skimmer - Pot + + + + + + + + + +
+ +