├── .gitignore
├── .readthedocs.yml
├── LICENSE.txt
├── README.md
├── apt.txt
├── docs
├── Makefile
├── _static
│ └── audio
│ │ ├── adsr.ogg
│ │ ├── sawtooth.ogg
│ │ ├── simple-fm-synth.ogg
│ │ ├── tb303.5.ogg
│ │ ├── violet.ogg
│ │ └── wobbly.ogg
├── _templates
│ └── layout.html
├── conf.py
├── images
│ ├── Atari-2600-Wood-4Sw-Set-small.png
│ ├── Commodore-64-Computer-FL-small.png
│ ├── adsr.png
│ ├── coordinate_systems_right_handed.png
│ ├── fm-sawtooth.png
│ ├── gate.png
│ ├── hello-world.gif
│ ├── lowpass.png
│ ├── pong.step0.png
│ ├── pong.step5.png
│ ├── pong.x3.step0.png
│ ├── pong.x3.step5.png
│ ├── power-mode.png
│ ├── resonance.png
│ ├── sawtooth.png
│ ├── ship1.png
│ ├── skybox-layout-small.png
│ ├── spaceship-3d.jpg
│ ├── spaceship.gif
│ ├── spaceship.jpg
│ └── spaceship_3d.gif
├── index.rst
├── make.bat
├── programmers_reference_guide
│ ├── api.rst
│ ├── appendices.rst
│ ├── getting_started.rst
│ ├── graphics-3d.rst
│ ├── graphics.rst
│ ├── introduction.rst
│ ├── rl.rst
│ ├── sound.rst
│ └── synthesis.rst
└── requirements.txt
├── examples
├── 01-hello-world.ipynb
├── 02-hello-jupylet.ipynb
├── 11-spaceship.ipynb
├── 12-spaceship-3d.ipynb
├── 13-lego-3d.ipynb
├── 14-piano.ipynb
├── 15-sonic.ipynb
├── 16-shadertoy-demo.ipynb
├── 17-spectrum-analyzer.ipynb
├── 21-pong.ipynb
├── 22-pong-RL.ipynb
├── fonts
│ ├── FreeLicense.txt
│ └── PetMe64.ttf
├── images
│ ├── alien.png
│ ├── keyboard.png
│ ├── moon.png
│ ├── ship1.png
│ ├── ship2.png
│ ├── stars.png
│ └── yellow-circle.png
├── lego_3d.py
├── piano.py
├── pong-start.state
├── pong.py
├── scenes
│ ├── lego
│ │ ├── lego.bin
│ │ ├── lego.blend
│ │ └── lego.gltf
│ └── moon
│ │ ├── TexturesCom_Leather_Plain_1K_albedo_blue.jpg
│ │ ├── TexturesCom_Leather_Plain_1K_normal.jpg
│ │ ├── TexturesCom_Leather_Plain_1K_roughness.png
│ │ ├── alien-moon.bin
│ │ ├── alien-moon.blend
│ │ ├── alien-moon.gltf
│ │ ├── eye.jpg
│ │ ├── lroc_color_poles_4k.jpg
│ │ ├── moon-normal-map.jpg
│ │ └── nebula
│ │ ├── nebulaBK.png
│ │ ├── nebulaDN.png
│ │ ├── nebulaFT.png
│ │ ├── nebulaLF.png
│ │ ├── nebulaRT.png
│ │ └── nebulaUP.png
├── shadertoy_demo.py
├── sounds
│ ├── VCSL
│ │ ├── README.md
│ │ └── Xylophone
│ │ │ ├── Xylophone - Medium Mallets.sfz
│ │ │ └── Xylophone
│ │ │ └── Medium Mallets
│ │ │ ├── Xylo_Medium_C4_ff_01_far.ogg
│ │ │ ├── Xylo_Medium_C4_pp_01_far.ogg
│ │ │ ├── Xylo_Medium_C5_ff_01_far.ogg
│ │ │ ├── Xylo_Medium_C5_pp_01_far.ogg
│ │ │ ├── Xylo_Medium_C6_ff_01_far.ogg
│ │ │ ├── Xylo_Medium_C6_pp_01_far.ogg
│ │ │ ├── Xylo_Medium_C7_ff_01_far.ogg
│ │ │ ├── Xylo_Medium_C7_pp_01_far.ogg
│ │ │ ├── Xylo_Medium_G3_ff_01_far.ogg
│ │ │ ├── Xylo_Medium_G3_pp_01_far.ogg
│ │ │ ├── Xylo_Medium_G4_ff_01_far.ogg
│ │ │ ├── Xylo_Medium_G4_pp_01_far.ogg
│ │ │ ├── Xylo_Medium_G5_ff_01_far.ogg
│ │ │ ├── Xylo_Medium_G5_pp_01_far.ogg
│ │ │ ├── Xylo_Medium_G6_ff_01_far.ogg
│ │ │ └── Xylo_Medium_G6_pp_01_far.ogg
│ └── pong-blip.wav
├── spaceship.py
├── spaceship_3d.py
├── spectrum_analyzer.py
└── spectrum_analyzer.state
├── jupylet
├── __init__.py
├── app.py
├── assets
│ ├── fonts
│ │ ├── SIL Open Font License.txt
│ │ └── SourceSerifPro-Bold.otf
│ ├── shaders
│ │ ├── default-fragment-shader.glsl
│ │ ├── default-vertex-shader.glsl
│ │ ├── shadertoy-wrapper.glsl
│ │ └── sprite.glsl
│ └── sounds
│ │ └── impulses
│ │ ├── InsidePiano.flac
│ │ ├── MaesHowe.flac
│ │ ├── README.md
│ │ └── StAndrewsChurch.flac
├── audio
│ ├── __init__.py
│ ├── bundle.py
│ ├── device.py
│ ├── effects.py
│ ├── filters.py
│ ├── midi.py
│ ├── note.py
│ ├── sample.py
│ ├── sound.py
│ └── synth.py
├── clock.py
├── collision.py
├── color.py
├── env.py
├── event.py
├── label.py
├── loader.py
├── lru.py
├── model.py
├── node.py
├── resource.py
├── rl.py
├── shadertoy.py
├── sprite.py
├── state.py
└── utils.py
├── setup.cfg
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 | .vscode/settings.json
131 | jpl.code-workspace
132 | tmp/
133 |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | # Build documentation in the docs/ directory with Sphinx
9 | sphinx:
10 | configuration: docs/conf.py
11 |
12 | python:
13 | version: 3.8
14 | install:
15 | - requirements: docs/requirements.txt
16 | - method: pip
17 | path: .
18 | system_packages: true
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2022, Nir Aides
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jupylet
2 |
3 | *Jupylet* is a Python library for programming 2D and 3D games, graphics, music
4 | and sound synthesizers, interactively in a Jupyter notebook. It is intended
5 | for three types of audiences:
6 |
7 | * Computer scientists, researchers, and students of deep reinforcement learning.
8 | * Musicians interested in sound synthesis and live music coding.
9 | * Kids and their parents interested in learning to program.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ## Jupylet for Kids
19 |
20 | A Jupyter notebook is in essence a laboratory for programming. It is the ideal
21 | environment for playing around with code, experimenting, and exploring ideas.
22 | It is used by professional machine learning scientists who come every day to
23 | play at work, so why not by kids?
24 |
25 | *Jupylet* is wonderfully easy to use for creating simple 2D and 3D games and
26 | music interactively and experimentally. Change a variable or a function and
27 | see how the game is affected immediately while running.
28 |
29 | ## Jupylet for Deep Reinforcement Learning
30 |
31 | *Jupylet* makes it is super easy to create and modify environments in which to
32 | experiment with deep reinforcement learning algorithms and it includes the API
33 | to programmatically control multiple simultaneous games and render thousands
34 | of frames per second.
35 |
36 | Consider for example the pong game included in this code base. With a few
37 | lines of code you can modify the colors of the game to experiment with transfer
38 | learning, or turn the game into 4-way pong with agents on all four sides of the
39 | game court to experiment with cooperation between multiple agents. And since you
40 | can modify the game interactively in Jupyter this process is not only easy but
41 | also fun.
42 |
43 | Check out the [*Programming Graphics*](https://jupylet.readthedocs.io/en/latest/programmers_reference_guide/graphics.html)
44 | and the [*Reinforcement Learning*](https://jupylet.readthedocs.io/en/latest/programmers_reference_guide/rl.html)
45 | chapters in the Jupylet Programmer's Reference Guide.
46 |
47 | ## Jupylet for Musicians
48 |
49 | *Jupylet* imports ideas and methods from machine learning into the domain
50 | of sound synthesis to easily let you create sound synthesizers as wild as you
51 | can dream up - it includes impulse response reverb effects, colored noise
52 | generators, resonant filters with cutoff frequency sweeping, oscillators with
53 | LFO modulation, multi sampled instruments, and much more... And all of it in
54 | pure Python for you to modify and experiment with.
55 |
56 | In addition *Jupylet* draws inspiration from the wonderful [*Sonic Pi*](https://sonic-pi.net/)
57 | and brings live loops and live music coding to Jupyter and Python. Hook up
58 | your MIDI keyboard and take off.
59 |
60 | Check out the [*Programming Sound and Music*](https://jupylet.readthedocs.io/en/latest/programmers_reference_guide/sound.html)
61 | and the [*Programming Synthesizers*](https://jupylet.readthedocs.io/en/latest/programmers_reference_guide/synthesis.html)
62 | chapters in the Jupylet Programmer's Reference Guide.
63 |
64 | ## Requirements
65 |
66 | *Jupylet* should run on Python 3.9 and up on Windows, Mac, and Linux.
67 |
68 | ## How to Install and Run Jupylet
69 |
70 | If you are new to Python, I recommend that you install and use the
71 | [Miniconda Python](https://docs.conda.io/en/latest/miniconda.html)
72 | distribution.
73 |
74 | **On Windows** – download and run the 64-bit installer for Python 3.11. Once
75 | Miniconda is installed press the `⊞ Winkey` and then type *Miniconda* and
76 | press the `Enter` key. This should open a small window that programmers call
77 | *console* or *shell* in which you can enter commands and run programs.
78 |
79 | **On macOS with M1 processor** – download and run "Miniconda3 macOS Apple M1 64-bit pkg"
80 | for Python 3.11. Once installed click the Spotlight icon `🔍` and in the search
81 | field type *terminal* and press the `Enter` key to open the console. Then you need
82 | to run the following command:
83 |
84 | pip install --extra-index https://github.com/nir/jupylet/releases/download/v0.9.2/ moderngl glcontext
85 |
86 | **On macOS with Intel processor** – download and run "Miniconda3 macOS Intel x86 64-bit pkg"
87 | for Python 3.11. Once installed click the Spotlight icon `🔍` and in the search
88 | field type *terminal* and press the `Enter` key to open the console.
89 |
90 | **On Linux** – download "Miniconda3 Linux 64-bit". This should download the file
91 | Miniconda3-latest-Linux-x86_64.sh. Install it by running the following command
92 | in a bash shell (once installed start a new bash shell):
93 |
94 | bash Miniconda3-latest-Linux-x86_64.sh
95 |
96 | ---
97 |
98 | Once Miniconda is installed it is time to install *jupylet* by typing the
99 | following command in the console:
100 |
101 | pip install jupylet
102 |
103 | Next, to run the example notebooks, download the *jupylet* source code.
104 | If you have [Git](https://git-scm.com/) installed type the following command:
105 |
106 | git clone https://github.com/nir/jupylet.git
107 |
108 | Alternatively, you can download the source code with the following command:
109 |
110 | python -m jupylet download
111 |
112 | Next, enter the *jupylet/examples/* directory with the change directory
113 | command:
114 |
115 | cd jupylet/examples/
116 |
117 | And start a jupyter notebook with:
118 |
119 | jupyter notebook 11-spaceship.ipynb
120 |
121 | Run the notebook by following the instructions in the notebook and a game
122 | canvas should appear with the spaceship example:
123 |
124 |
125 |
126 | Alternatively, you can run the same game as a Python script from the console
127 | with:
128 |
129 | python spaceship.py
130 |
131 | ## Documentation
132 |
133 | To get started with Jupylet head over to the *Jupylet Programmer's Reference
134 | Guide* which you can find at
135 | [jupylet.readthedocs.io](https://jupylet.readthedocs.io/).
136 |
137 | To complement the online guide check out the growing collection of
138 | [*example notebooks*](examples/) that you can download and run on your
139 | computer as explained above.
140 |
141 | ## Contact
142 |
143 | For questions and feedback send an email to [Nir Aides](mailto:nir.8bit@gmail.com) or [join the discussion](https://github.com/nir/jupylet/discussions).
144 |
145 | ## Spread the Word
146 |
147 | Jupylet is a new library and you can help it grow with a few clicks -
148 | if you like it let your friends know about it!
149 |
150 | ## Acknowledgements
151 |
152 | * [Einar Forselv](https://github.com/einarf) - The programmer behind ModernGL
153 | for his endless help in the trenches of OpenGL programming.
154 | * [Alban Fichet](https://afichet.github.io/) - For kindly licensing his
155 | sound visualizer Shadertoy as CC BY 4.0 license.
156 |
157 | ## What's New in Version 0.9.1
158 |
159 | * Support for Python 3.10 and Python 3.11 with MIDI functionality.
160 | * Seamlessly track changes to audio devices on macOS.
161 | * Workaround PIL api change - thanks to [@misolietavec](https://github.com/misolietavec).
162 | * Bug fixes.
163 |
164 | ## What's New in Version 0.8.9
165 |
166 | * Support for Python 3.10 and Python 3.11 - except for MIDI functionality.
167 | * Support for macOS M1.
168 | * Spectrum analyzer.
169 | * Bug fixes.
170 |
171 |
172 |
173 | ## What's New in Version 0.8.8
174 |
175 | * Support for Python 3.9.
176 |
177 | ## What's New in Version 0.8.7
178 |
179 | * Workaround auto-completion bug in Jupyter notebooks.
180 |
181 | ## What's New in Version 0.8.6
182 |
183 | * Support for rendering Shadertoy OpenGL shaders.
184 | [Shadertoy](https://www.shadertoy.com/) is an awesome online platform for
185 | programming and sharing OpenGL shaders online, and now you can
186 | [use and render shadertoy shaders in Jupylet!](https://jupylet.readthedocs.io/en/latest/programmers_reference_guide/graphics-3d.html#shadertoys)
187 |
188 |
--------------------------------------------------------------------------------
/apt.txt:
--------------------------------------------------------------------------------
1 | xvfb
2 | freeglut3-dev
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/_static/audio/adsr.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/_static/audio/adsr.ogg
--------------------------------------------------------------------------------
/docs/_static/audio/sawtooth.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/_static/audio/sawtooth.ogg
--------------------------------------------------------------------------------
/docs/_static/audio/simple-fm-synth.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/_static/audio/simple-fm-synth.ogg
--------------------------------------------------------------------------------
/docs/_static/audio/tb303.5.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/_static/audio/tb303.5.ogg
--------------------------------------------------------------------------------
/docs/_static/audio/violet.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/_static/audio/violet.ogg
--------------------------------------------------------------------------------
/docs/_static/audio/wobbly.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/_static/audio/wobbly.ogg
--------------------------------------------------------------------------------
/docs/_templates/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "!layout.html" %}
2 |
3 | {% block menu %}
4 | {{ super() }}
5 | INDEX
6 | {% endblock %}
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 |
16 | sys.path.insert(0, os.path.abspath('..'))
17 |
18 |
19 | # -- Project information -----------------------------------------------------
20 |
21 | project = 'Jupylet'
22 | copyright = '2022, Nir Aides'
23 | author = 'Nir Aides'
24 |
25 | # The full version, including alpha/beta/rc tags
26 | release = 'v0.9.2'
27 |
28 |
29 | # -- General configuration ---------------------------------------------------
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = [
35 | 'sphinx_rtd_theme',
36 | 'sphinx.ext.napoleon',
37 | 'sphinx.ext.autodoc',
38 | ]
39 |
40 | # Add any paths that contain templates here, relative to this directory.
41 | templates_path = ['_templates']
42 |
43 | # List of patterns, relative to source directory, that match files and
44 | # directories to ignore when looking for source files.
45 | # This pattern also affects html_static_path and html_extra_path.
46 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
47 |
48 |
49 | # -- Options for HTML output -------------------------------------------------
50 |
51 | # The theme to use for HTML and HTML Help pages. See the documentation for
52 | # a list of builtin themes.
53 | #
54 | #html_theme = 'alabaster'
55 | html_theme = "sphinx_rtd_theme"
56 |
57 | # Add any paths that contain custom static files (such as style sheets) here,
58 | # relative to this directory. They are copied after the builtin static files,
59 | # so a file named "default.css" will overwrite the builtin "default.css".
60 | html_static_path = ['_static']
61 |
62 | master_doc = 'index'
63 |
64 | autodoc_mock_imports = [
65 | 'scikit-image',
66 | 'sounddevice',
67 | 'matplotlib',
68 | 'soundfile',
69 | 'webcolors',
70 | 'gltflib',
71 | 'PyGLM',
72 | 'mido',
73 | ]
74 |
75 |
--------------------------------------------------------------------------------
/docs/images/Atari-2600-Wood-4Sw-Set-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/Atari-2600-Wood-4Sw-Set-small.png
--------------------------------------------------------------------------------
/docs/images/Commodore-64-Computer-FL-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/Commodore-64-Computer-FL-small.png
--------------------------------------------------------------------------------
/docs/images/adsr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/adsr.png
--------------------------------------------------------------------------------
/docs/images/coordinate_systems_right_handed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/coordinate_systems_right_handed.png
--------------------------------------------------------------------------------
/docs/images/fm-sawtooth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/fm-sawtooth.png
--------------------------------------------------------------------------------
/docs/images/gate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/gate.png
--------------------------------------------------------------------------------
/docs/images/hello-world.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/hello-world.gif
--------------------------------------------------------------------------------
/docs/images/lowpass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/lowpass.png
--------------------------------------------------------------------------------
/docs/images/pong.step0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/pong.step0.png
--------------------------------------------------------------------------------
/docs/images/pong.step5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/pong.step5.png
--------------------------------------------------------------------------------
/docs/images/pong.x3.step0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/pong.x3.step0.png
--------------------------------------------------------------------------------
/docs/images/pong.x3.step5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/pong.x3.step5.png
--------------------------------------------------------------------------------
/docs/images/power-mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/power-mode.png
--------------------------------------------------------------------------------
/docs/images/resonance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/resonance.png
--------------------------------------------------------------------------------
/docs/images/sawtooth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/sawtooth.png
--------------------------------------------------------------------------------
/docs/images/ship1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/ship1.png
--------------------------------------------------------------------------------
/docs/images/skybox-layout-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/skybox-layout-small.png
--------------------------------------------------------------------------------
/docs/images/spaceship-3d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/spaceship-3d.jpg
--------------------------------------------------------------------------------
/docs/images/spaceship.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/spaceship.gif
--------------------------------------------------------------------------------
/docs/images/spaceship.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/spaceship.jpg
--------------------------------------------------------------------------------
/docs/images/spaceship_3d.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/docs/images/spaceship_3d.gif
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. Jupylet documentation master file, created by
2 | sphinx-quickstart on Sat Feb 1 08:19:08 2020.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | JUPYLET PROGRAMMER'S REFERENCE GUIDE
7 | ====================================
8 |
9 |
10 | .. toctree::
11 | :maxdepth: 2
12 | :caption: TABLE OF CONTENTS
13 |
14 | programmers_reference_guide/introduction
15 | programmers_reference_guide/getting_started
16 | programmers_reference_guide/graphics
17 | programmers_reference_guide/graphics-3d
18 | programmers_reference_guide/sound
19 | programmers_reference_guide/synthesis
20 | programmers_reference_guide/rl
21 | programmers_reference_guide/api
22 | programmers_reference_guide/appendices
23 |
24 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/programmers_reference_guide/api.rst:
--------------------------------------------------------------------------------
1 | API REFERENCE
2 | =============
3 |
4 |
5 | Module jupylet.app
6 | ------------------
7 |
8 | Class App
9 | ^^^^^^^^^
10 |
11 | .. py:currentmodule:: jupylet.app
12 | .. autoclass:: App
13 |
14 |
15 | Methods
16 | """""""
17 |
18 | .. automethod:: App.run
19 | .. automethod:: App.stop
20 | .. automethod:: App.set_midi_sound
21 | .. automethod:: App.observe
22 | .. automethod:: App.scale_window_to
23 | .. automethod:: App.save_state
24 | .. automethod:: App.load_state
25 | .. automethod:: App.get_logging_widget
26 | .. automethod:: App.sonic_live_loop2
27 | .. automethod:: App.sonic_live_loop
28 | .. automethod:: App.run_me_every
29 | .. automethod:: App.run_me
30 | .. automethod:: App.mouse_position_event
31 | .. automethod:: App.mouse_press_event
32 | .. automethod:: App.mouse_release_event
33 | .. automethod:: App.key_event
34 | .. automethod:: App.render
35 | .. automethod:: App.event
36 | .. automethod:: App.close
37 | .. automethod:: App.load_program
38 |
39 |
40 | Properties
41 | """"""""""
42 |
43 | .. autoattribute:: App.width
44 | .. autoattribute:: App.height
45 |
46 |
47 | Module jupylet.sprite
48 | ---------------------
49 |
50 |
51 | Class Sprite
52 | ^^^^^^^^^^^^
53 |
54 | .. py:currentmodule:: jupylet.sprite
55 | .. autoclass:: Sprite
56 |
57 | Methods
58 | """""""
59 |
60 | .. automethod:: Sprite.render
61 | .. automethod:: Sprite.draw
62 | .. automethod:: Sprite.set_anchor
63 | .. automethod:: Sprite.collisions_with
64 | .. automethod:: Sprite.distance_to
65 | .. automethod:: Sprite.angle_to
66 | .. automethod:: Sprite.wrap_position
67 | .. automethod:: Sprite.clip_position
68 | .. automethod:: Sprite.get_state
69 | .. automethod:: Sprite.set_state
70 |
71 |
72 | Properties
73 | """"""""""
74 |
75 | .. autoattribute:: Sprite.scale
76 | .. autoattribute:: Sprite.x
77 | .. autoattribute:: Sprite.y
78 | .. autoattribute:: Sprite.angle
79 | .. autoattribute:: Sprite.width
80 | .. autoattribute:: Sprite.height
81 | .. autoattribute:: Sprite.image
82 | .. autoattribute:: Sprite.top
83 | .. autoattribute:: Sprite.right
84 | .. autoattribute:: Sprite.bottom
85 | .. autoattribute:: Sprite.left
86 | .. autoattribute:: Sprite.radius
87 | .. autoattribute:: Sprite.opacity
88 | .. autoattribute:: Sprite.color
89 |
90 | .. py:attribute:: Sprite.flip=True
91 |
92 | Flip the image upside down while rendering.
93 |
94 | :type: bool
95 |
96 | .. py:attribute:: Sprite.mipmap=True
97 |
98 | Compute mipmap textures when first loading the sprite image.
99 |
100 | :type: bool
101 |
102 | .. py:attribute:: Sprite.autocrop=False
103 |
104 | Auto crop the image to its bounding box when first loading the sprite image.
105 |
106 | :type: bool
107 |
108 | .. py:attribute:: Sprite.anisotropy=8.0
109 |
110 | Use anisotropic filtering when rendering the sprite image.
111 |
112 | :type: float
113 |
114 |
115 | Module jupylet.label
116 | --------------------
117 |
118 |
119 | Class Label
120 | ^^^^^^^^^^^
121 |
122 | .. py:currentmodule:: jupylet.label
123 | .. autoclass:: Label
124 |
125 |
126 | Properties
127 | """"""""""
128 |
129 | .. py:attribute:: Label.text
130 |
131 | Text to render as label.
132 |
133 | :type: str
134 |
135 | .. py:attribute:: Label.font_path
136 |
137 | Path to a true type or open type font.
138 |
139 | :type: str
140 |
141 | .. py:attribute:: Label.font_size=16
142 |
143 | Font size to use.
144 |
145 | :type: float
146 |
147 | .. py:attribute:: Label.line_height=1.2
148 |
149 | Determines the distance between lines.
150 |
151 | :type: float
152 |
153 | .. py:attribute:: Label.align='left'
154 |
155 | The desired alignment for the text label. May be one of 'left', 'center',
156 | and 'right'.
157 |
158 | :type: str
159 |
160 |
161 | Module jupylet.loader
162 | ---------------------
163 |
164 | .. py:currentmodule:: jupylet.loader
165 | .. py:module:: jupylet.loader
166 |
167 | .. autofunction:: load_blender_gltf
168 |
169 |
170 | Module jupylet.model
171 | --------------------
172 |
173 |
174 | Class Scene
175 | ^^^^^^^^^^^
176 |
177 | .. py:currentmodule:: jupylet.model
178 | .. autoclass:: Scene
179 |
180 |
181 | Methods
182 | """""""
183 |
184 | .. automethod:: Scene.draw
185 |
186 |
187 | Properties
188 | """"""""""
189 |
190 | .. py:attribute:: Scene.meshes
191 |
192 | A list of meshes.
193 |
194 | :type: list
195 |
196 | .. py:attribute:: Scene.lights
197 |
198 | A list of lights.
199 |
200 | :type: list
201 |
202 | .. py:attribute:: Scene.cameras
203 |
204 | A list of cameras.
205 |
206 | :type: list
207 |
208 | .. py:attribute:: Scene.materials
209 |
210 | A list of materials.
211 |
212 | :type: list
213 |
214 | .. py:attribute:: Scene.skybox
215 |
216 | A Skybox object.
217 |
218 | :type: Skybox
219 |
220 | .. py:attribute:: Scene.shadows
221 |
222 | Set to True to enable shadows.
223 |
224 | :type: bool
225 |
226 | .. py:attribute:: Scene.name
227 |
228 | Name of scene.
229 |
230 | :type: str
231 |
232 |
233 | Class Mesh
234 | ^^^^^^^^^^
235 |
236 | .. py:currentmodule:: jupylet.model
237 | .. autoclass:: Mesh
238 |
239 |
240 | Methods
241 | """""""
242 |
243 | .. automethod:: Mesh.move_local
244 | .. automethod:: Mesh.move_global
245 | .. automethod:: Mesh.rotate_local
246 | .. automethod:: Mesh.rotate_global
247 |
248 |
249 | Properties
250 | """"""""""
251 |
252 | .. autoattribute:: Mesh.front
253 | .. autoattribute:: Mesh.up
254 |
255 | .. py:attribute:: Mesh.primitives
256 |
257 | List of primitives the mesh consists of.
258 |
259 | :type: list
260 |
261 | .. py:attribute:: Mesh.children
262 |
263 | List of child meshes.
264 |
265 | :type: list
266 |
267 | .. py:attribute:: Mesh.hide
268 |
269 | Set to False to hide mesh from view.
270 |
271 | :type: bool
272 |
273 | .. py:attribute:: Mesh.name
274 |
275 | Name of mesh.
276 |
277 | :type: str
278 |
279 |
280 | Module jupylet.audio
281 | --------------------
282 |
283 | .. py:currentmodule:: jupylet.audio
284 | .. py:module:: jupylet.audio
285 |
286 |
287 | .. autofunction:: sonic_py
288 | .. autofunction:: set_bpm
289 | .. autofunction:: set_note_value
290 | .. autofunction:: use
291 | .. autofunction:: play
292 | .. autofunction:: sleep
293 |
294 |
295 | Module jupylet.audio.sound
296 | --------------------------
297 |
298 |
299 | Class Sound
300 | ^^^^^^^^^^^
301 |
302 | .. py:currentmodule:: jupylet.audio.sound
303 | .. autoclass:: Sound
304 |
305 |
306 | Methods
307 | """""""
308 |
309 | .. automethod:: Sound.play
310 | .. automethod:: Sound.play_poly
311 | .. automethod:: Sound.play_release
312 | .. automethod:: Sound.set_effects
313 | .. automethod:: Sound.get_effects
314 |
315 |
316 | Properties
317 | """"""""""
318 |
319 | .. autoattribute:: Sound.note
320 | .. autoattribute:: Sound.key
321 |
322 | .. py:attribute:: Sound.freq
323 |
324 | Fundamental requency of sound object.
325 |
326 | :type: float
327 |
328 | .. py:attribute:: Sound.amp
329 |
330 | Output amplitude - a value between 0 and 1.
331 |
332 | :type: float
333 |
334 | .. py:attribute:: Sound.pan
335 |
336 | Balance between left (-1) and right (1) output channels.
337 |
338 | :type: float
339 |
340 |
341 | Class Oscillator
342 | ^^^^^^^^^^^^^^^^
343 |
344 | .. py:currentmodule:: jupylet.audio.sound
345 | .. autoclass:: Oscillator
346 |
347 |
348 | Methods
349 | """""""
350 |
351 | .. automethod:: Oscillator.forward
352 |
353 |
354 | Properties
355 | """"""""""
356 |
357 | .. py:attribute:: Oscillator.shape
358 |
359 | Waveform to generate - one of `sine`, `triangle`, `sawtooth`, or `square`.
360 |
361 | :type: str
362 |
363 | .. py:attribute:: Oscillator.sign
364 |
365 | Set to -1 to flip sawtooth waveform upside down.
366 |
367 | :type: float
368 |
369 | .. py:attribute:: Oscillator.duty
370 |
371 | The fraction of the square waveform cycle its value is 1.
372 |
373 | :type: float
374 |
375 |
376 | Class LatencyGate
377 | ^^^^^^^^^^^^^^^^^
378 |
379 | .. py:currentmodule:: jupylet.audio.sound
380 | .. autoclass:: LatencyGate
381 |
382 |
383 | Methods
384 | """""""
385 |
386 | .. automethod:: LatencyGate.open
387 | .. automethod:: LatencyGate.close
388 |
389 |
390 | Class GatedSound
391 | ^^^^^^^^^^^^^^^^
392 |
393 | .. py:currentmodule:: jupylet.audio.sound
394 | .. autoclass:: GatedSound
395 |
396 |
397 | Methods
398 | """""""
399 |
400 | .. automethod:: GatedSound.play
401 | .. automethod:: GatedSound.play_poly
402 | .. automethod:: GatedSound.play_release
403 |
404 |
405 | Module jupylet.audio.sample
406 | ---------------------------
407 |
408 |
409 | Class Sample
410 | ^^^^^^^^^^^^
411 |
412 | .. py:currentmodule:: jupylet.audio.sample
413 | .. autoclass:: Sample
414 |
415 |
416 | Methods
417 | """""""
418 |
419 | .. automethod:: Sample.load
420 |
421 |
422 | Properties
423 | """"""""""
424 |
425 | .. py:attribute:: Sample.path
426 |
427 | Path to audio file.
428 |
429 | :type: str
430 |
431 |
--------------------------------------------------------------------------------
/docs/programmers_reference_guide/appendices.rst:
--------------------------------------------------------------------------------
1 | APPENDICES
2 | ==========
3 |
4 | A Few Words About Notebooks
5 | ---------------------------
6 |
7 | A Jupyter notebook lets you mix formatted text with computer code and graphics.
8 | Jupyter notebooks belong to the more general category of `computational
9 | notebooks `_.
10 |
11 | Computational notebooks are the modern version of the traditional scientist's
12 | laboratory notebook. Such notebooks have been used by scientists for hundreds
13 | of years to keep track of their ideas and experiments in physics, chemistry,
14 | biology, etc...
15 |
16 | A very interesting thing about Jupyter notebooks is that when it comes to
17 | computer science and data science they mix the laboratory and the notebook into
18 | one. That is, a Jupyter notebook is also a computing laboratory since you can
19 | actually run code in them!
20 |
21 | Here is a very special example of a traditional laboratory notebook. A page
22 | from `Galileo Galilei's `_
23 | notebook in which he documented his first observations of the moons of Jupiter
24 | 400 years ago:
25 |
26 | .. image:: https://upload.wikimedia.org/wikipedia/commons/thumb/a/a6/Galileo_manuscript.png/419px-Galileo_manuscript.png
27 |
28 | You can think of it as the first Jupiter notebook 😎 and if you know Italian
29 | let me know what it says.
30 |
31 |
32 | The Atari 2600
33 | --------------
34 |
35 | I grew up playing an `Atari 2600 `_
36 | like the one shown in this picture:
37 |
38 | .. image:: ../images/Atari-2600-Wood-4Sw-Set-small.png
39 |
40 | I spent endless hours of fun playing so many of its games.
41 |
42 | In 2013 Deepmind published a `Reinforcement Learning` algorithm that surpassed
43 | human level performance on some of its classic games. The video of their agent
44 | learning to play and then master the game of `Breakout `_
45 | stunned the world:
46 |
47 | .. raw:: html
48 |
49 |
50 |
51 |
52 |
53 | Since then Atari games are used as a benchmark for `Reinforcement Learning`
54 | algorithms.
55 |
56 | When I started learning `Deep Learning` and `Deep Reinforcement
57 | Learning` I found myself spending too much time trying to customize
58 | `the computer code running the Atari games `_,
59 | and so it made perfect sense to me to spend a whole lot more time, I mean,
60 | way more time, programming a completely new environment, and so Jupylet came
61 | to be.
62 |
63 |
64 | The Commodore 64
65 | ----------------
66 |
67 | I still remember the day my dad brought home the `Commodore 64 `_
68 | and we connected it to the TV set for the first time. It is such a beauty -
69 | take a look:
70 |
71 | .. image:: ../images/Commodore-64-Computer-FL-small.png
72 |
73 | Just as with the `Atari 2600` I spent countless hours playing its wonderful
74 | games, which made my parents worry quite a bit 😎 but like many other kids I
75 | soon tried to program it, and then to program basic games with graphics and
76 | sound, and soon I got carried away down the rabbit hole of computation into
77 | programming wonderland.
78 |
79 | It is a wonder land in which you can turn any idea that pops into your head
80 | into a `something` that works and sounds and plays just like you dreamed it.
81 |
82 | The `Commodore 64` was the perfect conductor for that kind of magic. Soon
83 | after I started programming Jupylet I realized that what I really wanted is
84 | to try to recreate this kind of environment for today's kids.
85 |
86 |
87 | The 6581 SID Chip
88 | -----------------
89 |
90 | The `Commodore 64` had a gem of a chip hiding inside it - the
91 | `6581 SID chip `_
92 | which was a very sophisticated sound synthesizer for its time:
93 |
94 | .. raw:: html
95 |
96 |
97 |
98 |
99 |
100 | As I was programming the sound synthesis framework of Jupylet I revisited the
101 | epic `Commodore 64 Programmers's Reference Guide `_
102 | and I was stunned to see how advanced it really was.
103 |
104 | It had three independant waveform generators that could generate `sine`,
105 | `triangle`, `sawtooth`, `pulse` waveform with variable duty, and `white noise`,
106 | and you could use the amplitude of one to modulate the frequency of another!
107 | It had a classic ADSR envelope generator, and it had a filter that you could
108 | use as `lowpass`, `highpass`, or `bandpass` and sweep its `cutoff` frequency
109 | dynamically.
110 |
111 | And its epic guide explained all of it clearly in a language that a child could
112 | understand and in depth that a musician would find useful and professional
113 | programmers appreciate, starting with what sound waves really are,
114 | explaining fundamental frequencies and harmonics, and all the way to describing
115 | frequency sweeping and how to code the computer to control all of the
116 | synthesizer's parameters dynamically.
117 |
118 | It was recently included in `IEEE Spectrum Chip Hall of Fame `_
119 | and nearly 40 years later it still has a following of fans.
120 |
121 |
--------------------------------------------------------------------------------
/docs/programmers_reference_guide/getting_started.rst:
--------------------------------------------------------------------------------
1 | GETTING STARTED
2 | ===============
3 |
4 | How to Install and Run Jupylet
5 | ------------------------------
6 |
7 | If you are new to Python, I recommend that you install and use the
8 | `Miniconda Python `_
9 | distribution.
10 |
11 | **On Windows** -- download and run the 64-bit installer for Python 3.11. Once
12 | Miniconda is installed press the :guilabel:`⊞ Winkey` and then type
13 | *Miniconda* and press the :guilabel:`Enter` key. This should open a small
14 | window that programmers call *console* or *shell* in which you can enter
15 | commands and run programs.
16 |
17 | **On macOS with M1 processor** -- download and run "Miniconda3 macOS Apple M1 64-bit pkg"
18 | for Python 3.11. Once installed click the Spotlight icon :guilabel:`🔍` and
19 | in the search field type *terminal* and press the :guilabel:`Enter` key to
20 | open the console.
21 |
22 | **On macOS with Intel processor** -- download and run "Miniconda3 macOS Intel x86 64-bit pkg"
23 | for Python 3.11. Once installed click the Spotlight icon :guilabel:`🔍` and
24 | in the search field type *terminal* and press the :guilabel:`Enter` key to
25 | open the console.
26 |
27 | **On Linux** -- download "Miniconda3 Linux 64-bit". This should download the file
28 | Miniconda3-latest-Linux-x86_64.sh. Install it by running the following command
29 | in a bash shell (once installed start a new bash shell):
30 |
31 | .. code-block:: bash
32 |
33 | bash Miniconda3-latest-Linux-x86_64.sh
34 |
35 | ------------
36 |
37 | Once Miniconda is installed it is time to install *jupylet* by typing the
38 | following command in the console:
39 |
40 | .. code-block:: bash
41 |
42 | pip install jupylet
43 |
44 | Next, to run the example notebooks download the *jupylet* source code. If
45 | you have `Git `_ installed type the following command:
46 |
47 | .. code-block:: bash
48 |
49 | git clone https://github.com/nir/jupylet.git
50 |
51 | Alternatively, you can download the source code with the following command:
52 |
53 | .. code-block:: bash
54 |
55 | python -m jupylet download
56 |
57 | Next, enter the *jupylet/examples/* directory with the change directory
58 | command:
59 |
60 | .. code-block:: bash
61 |
62 | cd jupylet/examples/
63 |
64 | And start a jupyter notebook with:
65 |
66 | .. code-block:: bash
67 |
68 | jupyter notebook 11-spaceship.ipynb
69 |
70 | Run the notebook by following the instructions in the notebook and a game
71 | canvas should appear with the spaceship example:
72 |
73 | .. image:: ../images/spaceship.gif
74 |
75 | Alternatively, you can run the same game as a Python script from the console
76 | with:
77 |
78 | .. code-block:: bash
79 |
80 | python spaceship.py
81 |
82 | The Python Programming Language
83 | -------------------------------
84 |
85 | Python is an awesome programming language. It is both simple for kids to
86 | learn and powerful enough to be `one of the most popular programming languages
87 | `_ among computer scientists and
88 | programmers.
89 |
90 | However, this reference guide is not designed to teach the Python programming
91 | language. If you don't already have a working knowlege of Python and how to
92 | use it to program, I would like to suggest a few resources that may help you
93 | get started:
94 |
95 | - `Microsoft's introduction to Python `_
96 | \- Microsoft has a long tradition of publishing good guides to programming
97 | languages and this tutorial appears to be in line with this tradition.
98 | However, their Azure Cloud Shell is unfortunately a distraction. You would
99 | be better off trying out their exercises in Python's own `online shell `_.
100 |
101 | - `Python's own tutorial `_
102 | \- Perhaps not as didactic as Microsoft's guide, but it is a good idea to
103 | get familiar with Python's official documentation.
104 |
105 | - `Mike Dane's Learn Python Yotube tutorial `_
106 | \- Appears to be a good didactic introduction to Python.
107 |
108 | These guides will instruct you how to start a python interpreter where you
109 | can type and run Python code. You may do that, but once you gain a little bit
110 | of confidence or if you feel adventurous try starting a Jupyter notebook
111 | instead of a simple python interpreter.
112 |
113 | To do that start the Miniconda Prompt
114 | `as explained above <#how-to-install-and-run-jupylet>`_, then change
115 | directory into the *jupylet/examples/* directory and start a new notebook by
116 | typing:
117 |
118 | .. code-block:: bash
119 |
120 | jupyter notebook 01-hello-world.ipynb
121 |
122 | Jupyter Notebooks
123 | -----------------
124 |
125 | Jupyter notebooks are awesome but they can be a little confusing at
126 | first. Here are a few resources that explain how to use them:
127 |
128 | - `examples/01-hello-world.ipynb `_
129 | notebook contains a basic introduction to Jupyter notebooks. Check it out.
130 |
131 | - `Running Code `_
132 | \- This is a Jupyter notebook explaining how to use Jupyter notebooks 🙂.
133 | It is in fact a live notebook running in a web service called mybinder. The
134 | first time you click it may take a moment to start, so give it a moment.
135 | Since it is "live" you can play around with it. It works!
136 |
137 | - `Jupyter's documentation `_
138 | \- There's a whole lot of text in there.
139 |
140 |
--------------------------------------------------------------------------------
/docs/programmers_reference_guide/introduction.rst:
--------------------------------------------------------------------------------
1 | INTRODUCTION
2 | ============
3 |
4 | *Jupylet* is a Python library for programming 2D and 3D games, graphics,
5 | music and sound synthesizers, interactively in a Jupyter notebook. It is
6 | intended for three types of audiences:
7 |
8 | * Computer scientists, researchers, and students of deep reinforcement learning.
9 | * Musicians interested in sound synthesis and live music coding.
10 | * Kids and their parents interested in learning to program.
11 |
12 | .. image:: ../images/spaceship.gif
13 | :width: 36 %
14 | .. image:: ../images/spaceship_3d.gif
15 | :width: 54 %
16 |
17 | Jupylet for Kids
18 | ----------------
19 |
20 | A Jupyter notebook is in essence a laboratory for programming. It is the ideal
21 | environment for playing around with code, experimenting, and exploring ideas.
22 | It is used by professional machine learning scientists who come every day to
23 | play at work, so why not by kids?
24 |
25 | *Jupylet* is wonderfully easy to use for creating simple 2D and 3D games and
26 | music interactively and experimentally. Change a variable or a function and
27 | see how the game is affected immediately while it is running.
28 | :any:`Let's get started!`
29 |
30 |
31 | Jupylet for Deep Reinforcement Learning
32 | ---------------------------------------
33 |
34 | *Jupylet* makes it is super easy to create and modify environments in which to
35 | experiment with deep reinforcement learning algorithms and it includes the API
36 | to programmatically control multiple simultaneous games and render thousands
37 | of frames per second.
38 |
39 | Consider for example the pong game included in this repository. With a few
40 | lines of code you can modify the colors of the game to experiment with transfer
41 | learning, or turn the game into 4-way pong with agents on all four sides of the
42 | game court to experiment with cooperation between multiple agents. And since you
43 | can modify the game interactively in Jupyter this process is not only easy but
44 | also fun.
45 |
46 | See the :any:`Programming Graphics` and the
47 | :any:`Reinforcement Learning` chapters for more information.
48 |
49 |
50 | Jupylet for Musicians
51 | ---------------------
52 |
53 | *Jupylet* imports ideas and methods from machine learning into the domain
54 | of sound synthesis to easily let you create sound synthesizers as wild as you
55 | can dream up - it includes impulse response reverb effects, colored noise
56 | generators, resonant filters with cutoff frequency sweeping, oscillators with
57 | LFO modulation, multi sampled instruments, and much more... And all of it in
58 | pure Python for you to modify and experiment with.
59 |
60 | In addition *Jupylet* draws inspiration from the wonderful `Sonic Pi `_
61 | and brings live loops and live music coding to Jupyter and Python. Hook up
62 | your MIDI keyboard and take off.
63 |
64 | See the :any:`Programming Sound and Music` and the
65 | :any:`Programming Synthesizers` chapters for more information.
66 |
67 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | Sphinx==5.3.0
2 | sphinx-rtd-theme==1.1.1
3 | Jinja2==3.1.3
--------------------------------------------------------------------------------
/examples/01-hello-world.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Hello, World!\n",
8 | "\n",
9 | "Let's start with a traditional first program called [Hello, World!](https://en.wikipedia.org/wiki/%22Hello,_World!%22_program) \n",
10 | "\n",
11 | "Here is how it looked like 45 years ago when it was written for the first time by Brian Kernighan for the C programming language:\n",
12 | "\n",
13 | "```c\n",
14 | "main( ) {\n",
15 | " printf(\"hello, world\\n\");\n",
16 | "}\n",
17 | "```\n",
18 | "\n",
19 | "Now it's your turn. \n",
20 | "\n",
21 | "Jupyter notebooks are made of cells. The cell in which this text is written is called a markdown cell. Markdown cells can be used to describe and document your code. \n",
22 | "\n",
23 | "The cell below is a called a code cell. Code cells can be used to run code. The cell below contains the python code for *Hello, World!* \n",
24 | "\n",
25 | "To run the cell below press the `Down` arrow key or click the button in the toolbar above to select it. A blue border around the cell will indicate it is now in focus. Then press `Shift-Enter` or click the button in the toolbar above to run it. If you do it correctly the code will print the famous words:"
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "execution_count": 1,
31 | "metadata": {},
32 | "outputs": [
33 | {
34 | "name": "stdout",
35 | "output_type": "stream",
36 | "text": [
37 | "hello, world\n"
38 | ]
39 | }
40 | ],
41 | "source": [
42 | "print('hello, world')"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": []
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "metadata": {},
55 | "source": [
56 | "Great work! \n",
57 | "\n",
58 | "Now, click the `Help` menu above and select `User Interface Tour` from the drop down list for a short introduction to the user interface of Jupyter. \n",
59 | "\n",
60 | "To edit the content of a cell select it with the `Up` and `Down` arrow keys and press `Enter`. The color of the cell border should change from blue to green to indicate that the cell is in edit mode. \n",
61 | "\n",
62 | "Edit the content of the cell below. Change it to `2 + 2`. When you are done press `Esc` to switch back to command mode or press `Shift-Enter` to run the cell. "
63 | ]
64 | },
65 | {
66 | "cell_type": "code",
67 | "execution_count": 2,
68 | "metadata": {},
69 | "outputs": [
70 | {
71 | "data": {
72 | "text/plain": [
73 | "2"
74 | ]
75 | },
76 | "execution_count": 2,
77 | "metadata": {},
78 | "output_type": "execute_result"
79 | }
80 | ],
81 | "source": [
82 | "1 + 1"
83 | ]
84 | },
85 | {
86 | "cell_type": "code",
87 | "execution_count": null,
88 | "metadata": {},
89 | "outputs": [],
90 | "source": []
91 | },
92 | {
93 | "cell_type": "markdown",
94 | "metadata": {},
95 | "source": [
96 | "Great work!\n",
97 | "\n",
98 | "Now, explore the menubar above to see how you can manipulate cells around. There are keyboard shortcuts that you can use for most actions.\n",
99 | "\n",
100 | "I have prepared a few more cells for you to play around with. You can use this notebook to practice your skills. If you are new to Python I recommend [Microsoft's introduction to Python](https://docs.microsoft.com/en-us/learn/modules/intro-to-python/1-introduction)."
101 | ]
102 | },
103 | {
104 | "cell_type": "code",
105 | "execution_count": null,
106 | "metadata": {},
107 | "outputs": [],
108 | "source": []
109 | },
110 | {
111 | "cell_type": "code",
112 | "execution_count": 3,
113 | "metadata": {},
114 | "outputs": [],
115 | "source": [
116 | "a = 5"
117 | ]
118 | },
119 | {
120 | "cell_type": "code",
121 | "execution_count": 4,
122 | "metadata": {},
123 | "outputs": [
124 | {
125 | "data": {
126 | "text/plain": [
127 | "6"
128 | ]
129 | },
130 | "execution_count": 4,
131 | "metadata": {},
132 | "output_type": "execute_result"
133 | }
134 | ],
135 | "source": [
136 | "a + 1"
137 | ]
138 | },
139 | {
140 | "cell_type": "code",
141 | "execution_count": 5,
142 | "metadata": {},
143 | "outputs": [
144 | {
145 | "name": "stdout",
146 | "output_type": "stream",
147 | "text": [
148 | "0\n",
149 | "1\n",
150 | "2\n",
151 | "3\n",
152 | "4\n"
153 | ]
154 | }
155 | ],
156 | "source": [
157 | "for i in range(5):\n",
158 | " print(i)"
159 | ]
160 | },
161 | {
162 | "cell_type": "code",
163 | "execution_count": null,
164 | "metadata": {},
165 | "outputs": [],
166 | "source": []
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": 6,
171 | "metadata": {},
172 | "outputs": [],
173 | "source": [
174 | "def greet(name):\n",
175 | " print('Hello, ' + name + '!')"
176 | ]
177 | },
178 | {
179 | "cell_type": "code",
180 | "execution_count": 7,
181 | "metadata": {},
182 | "outputs": [
183 | {
184 | "name": "stdout",
185 | "output_type": "stream",
186 | "text": [
187 | "Hello, Dave!\n"
188 | ]
189 | }
190 | ],
191 | "source": [
192 | "greet('Dave')"
193 | ]
194 | },
195 | {
196 | "cell_type": "markdown",
197 | "metadata": {},
198 | "source": [
199 | "
"
200 | ]
201 | }
202 | ],
203 | "metadata": {
204 | "kernelspec": {
205 | "display_name": "Python 3",
206 | "language": "python",
207 | "name": "python3"
208 | },
209 | "language_info": {
210 | "codemirror_mode": {
211 | "name": "ipython",
212 | "version": 3
213 | },
214 | "file_extension": ".py",
215 | "mimetype": "text/x-python",
216 | "name": "python",
217 | "nbconvert_exporter": "python",
218 | "pygments_lexer": "ipython3",
219 | "version": "3.8.5"
220 | }
221 | },
222 | "nbformat": 4,
223 | "nbformat_minor": 2
224 | }
225 |
--------------------------------------------------------------------------------
/examples/02-hello-jupylet.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Hello, Jupylet!\n",
8 | "\n",
9 | "This notebook demonstrates creating a simple canvas with a scrolling banner."
10 | ]
11 | },
12 | {
13 | "cell_type": "markdown",
14 | "metadata": {},
15 | "source": [
16 | "### How to use Jupyter notebooks\n",
17 | "\n",
18 | "If you are unfamiliar with Jupyter notebooks open the [*01-hello-world.ipynb*](./01-hello-world.ipynb) notebook which introduces the Jupyter notebook user inteface."
19 | ]
20 | },
21 | {
22 | "cell_type": "markdown",
23 | "metadata": {},
24 | "source": [
25 | "### Run the game\n",
26 | "\n",
27 | "Run this notebook and see what happens. Later when you are ready come back here to read the game code.\n",
28 | "\n",
29 | "To run the game click `Cell` in the menubar above and selct `Run All` from the drop down list. The notebook will scroll to its bottom where a game canvas should appear with the scrolling banner."
30 | ]
31 | },
32 | {
33 | "cell_type": "markdown",
34 | "metadata": {},
35 | "source": [
36 | "### Prerequisites\n",
37 | "\n",
38 | "To understand this code you need to know about Python imports, functions, and classes."
39 | ]
40 | },
41 | {
42 | "cell_type": "code",
43 | "execution_count": null,
44 | "metadata": {},
45 | "outputs": [],
46 | "source": []
47 | },
48 | {
49 | "cell_type": "code",
50 | "execution_count": 1,
51 | "metadata": {},
52 | "outputs": [],
53 | "source": [
54 | "import sys\n",
55 | "import os"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": 2,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "sys.path.insert(0, os.path.abspath('./..'))"
65 | ]
66 | },
67 | {
68 | "cell_type": "code",
69 | "execution_count": 3,
70 | "metadata": {},
71 | "outputs": [],
72 | "source": [
73 | "from jupylet.label import Label\n",
74 | "from jupylet.app import App"
75 | ]
76 | },
77 | {
78 | "cell_type": "code",
79 | "execution_count": null,
80 | "metadata": {},
81 | "outputs": [],
82 | "source": []
83 | },
84 | {
85 | "cell_type": "markdown",
86 | "metadata": {},
87 | "source": [
88 | "Create a game application object:"
89 | ]
90 | },
91 | {
92 | "cell_type": "code",
93 | "execution_count": 4,
94 | "metadata": {},
95 | "outputs": [],
96 | "source": [
97 | "app = App(width=320, height=64)"
98 | ]
99 | },
100 | {
101 | "cell_type": "markdown",
102 | "metadata": {},
103 | "source": [
104 | "Load a text label with the text *'hello, world'* and position it just outside the right hand side of the canvas:"
105 | ]
106 | },
107 | {
108 | "cell_type": "code",
109 | "execution_count": 5,
110 | "metadata": {},
111 | "outputs": [],
112 | "source": [
113 | "hello = Label('hello, world', color='cyan', font_size=32, x=app.width, y=22)"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {},
119 | "source": [
120 | "Create a function to update the label position. The `@app.run_me_every(1/24)` decorator will make sure it is run 24 times each second:"
121 | ]
122 | },
123 | {
124 | "cell_type": "code",
125 | "execution_count": 6,
126 | "metadata": {},
127 | "outputs": [],
128 | "source": [
129 | "@app.run_me_every(1/24)\n",
130 | "def scroll(ct, dt):\n",
131 | " hello.x -= dt * 48\n",
132 | " if hello.right < 0:\n",
133 | " hello.x = app.width"
134 | ]
135 | },
136 | {
137 | "cell_type": "markdown",
138 | "metadata": {},
139 | "source": [
140 | "Create a function to render a game frame to the canvas:"
141 | ]
142 | },
143 | {
144 | "cell_type": "code",
145 | "execution_count": 7,
146 | "metadata": {},
147 | "outputs": [],
148 | "source": [
149 | "@app.event\n",
150 | "def render(ct, dt):\n",
151 | " app.window.clear()\n",
152 | " hello.draw()"
153 | ]
154 | },
155 | {
156 | "cell_type": "markdown",
157 | "metadata": {},
158 | "source": [
159 | "And finally start the game:"
160 | ]
161 | },
162 | {
163 | "cell_type": "code",
164 | "execution_count": 8,
165 | "metadata": {
166 | "scrolled": false
167 | },
168 | "outputs": [
169 | {
170 | "data": {
171 | "application/vnd.jupyter.widget-view+json": {
172 | "model_id": "d29d131b754b4e83a6140adf39068988",
173 | "version_major": 2,
174 | "version_minor": 0
175 | },
176 | "text/plain": [
177 | "Image(value=b'\\xff\\xd8\\xff\\xe0\\x00\\x10JFIF\\x00\\x01\\x01\\x00\\x00\\x01\\x00\\x01\\x00\\x00\\xff\\xdb\\x00C\\x00\\x08\\x06\\x0…"
178 | ]
179 | },
180 | "metadata": {},
181 | "output_type": "display_data"
182 | }
183 | ],
184 | "source": [
185 | "app.run()"
186 | ]
187 | },
188 | {
189 | "cell_type": "code",
190 | "execution_count": null,
191 | "metadata": {},
192 | "outputs": [],
193 | "source": []
194 | }
195 | ],
196 | "metadata": {
197 | "kernelspec": {
198 | "display_name": "Python 3",
199 | "language": "python",
200 | "name": "python3"
201 | },
202 | "language_info": {
203 | "codemirror_mode": {
204 | "name": "ipython",
205 | "version": 3
206 | },
207 | "file_extension": ".py",
208 | "mimetype": "text/x-python",
209 | "name": "python",
210 | "nbconvert_exporter": "python",
211 | "pygments_lexer": "ipython3",
212 | "version": "3.8.5"
213 | }
214 | },
215 | "nbformat": 4,
216 | "nbformat_minor": 2
217 | }
218 |
--------------------------------------------------------------------------------
/examples/16-shadertoy-demo.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "## Shadertoys\n",
8 | "\n",
9 | "This notebook demostrates how to display [shadertoys](https://www.shadertoy.com/). Check out the documentation for more information: \n",
10 | "https://jupylet.readthedocs.io/en/latest/programmers_reference_guide/graphics-3d.html#shadertoys"
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": null,
16 | "metadata": {},
17 | "outputs": [],
18 | "source": []
19 | },
20 | {
21 | "cell_type": "code",
22 | "execution_count": 1,
23 | "metadata": {},
24 | "outputs": [],
25 | "source": [
26 | "import sys\n",
27 | "import os"
28 | ]
29 | },
30 | {
31 | "cell_type": "code",
32 | "execution_count": 2,
33 | "metadata": {},
34 | "outputs": [],
35 | "source": [
36 | "sys.path.insert(0, os.path.abspath('./..'))"
37 | ]
38 | },
39 | {
40 | "cell_type": "code",
41 | "execution_count": 3,
42 | "metadata": {},
43 | "outputs": [],
44 | "source": [
45 | "from jupylet.app import App\n",
46 | "from jupylet.label import Label\n",
47 | "from jupylet.shadertoy import Shadertoy"
48 | ]
49 | },
50 | {
51 | "cell_type": "code",
52 | "execution_count": 4,
53 | "metadata": {},
54 | "outputs": [],
55 | "source": [
56 | "from jupylet.audio.bundle import *"
57 | ]
58 | },
59 | {
60 | "cell_type": "code",
61 | "execution_count": null,
62 | "metadata": {},
63 | "outputs": [],
64 | "source": []
65 | },
66 | {
67 | "cell_type": "code",
68 | "execution_count": 5,
69 | "metadata": {},
70 | "outputs": [],
71 | "source": [
72 | "app = App(width=533, height=300)"
73 | ]
74 | },
75 | {
76 | "cell_type": "code",
77 | "execution_count": null,
78 | "metadata": {},
79 | "outputs": [],
80 | "source": []
81 | },
82 | {
83 | "cell_type": "markdown",
84 | "metadata": {},
85 | "source": [
86 | "### Star Nest Shadertoy by Pablo Roman Andrioli\n",
87 | "\n",
88 | "The code in the following cell is of a [beautiful shadertoy by Pablo Roman Andrioli](https://www.shadertoy.com/view/XlfGRj). It is available under the MIT License. "
89 | ]
90 | },
91 | {
92 | "cell_type": "code",
93 | "execution_count": 6,
94 | "metadata": {},
95 | "outputs": [],
96 | "source": [
97 | "st = Shadertoy(\"\"\"\n",
98 | "\n",
99 | " // Star Nest by Pablo Roman Andrioli\n",
100 | "\n",
101 | " // This content is under the MIT License.\n",
102 | "\n",
103 | " #define iterations 17\n",
104 | " #define formuparam 0.53\n",
105 | "\n",
106 | " #define volsteps 20\n",
107 | " #define stepsize 0.1\n",
108 | "\n",
109 | " #define zoom 0.800\n",
110 | " #define tile 0.850\n",
111 | " #define speed 0.010 \n",
112 | "\n",
113 | " #define brightness 0.0015\n",
114 | " #define darkmatter 0.300\n",
115 | " #define distfading 0.730\n",
116 | " #define saturation 0.850\n",
117 | "\n",
118 | "\n",
119 | " void mainImage( out vec4 fragColor, in vec2 fragCoord )\n",
120 | " {\n",
121 | " //get coords and direction\n",
122 | " vec2 uv=fragCoord.xy/iResolution.xy-.5;\n",
123 | " uv.y*=iResolution.y/iResolution.x;\n",
124 | " vec3 dir=vec3(uv*zoom,1.);\n",
125 | " float time=iTime*speed+.25;\n",
126 | "\n",
127 | " //mouse rotation\n",
128 | " float a1=.5+iMouse.x/iResolution.x*2.;\n",
129 | " float a2=.8+iMouse.y/iResolution.y*2.;\n",
130 | " mat2 rot1=mat2(cos(a1),sin(a1),-sin(a1),cos(a1));\n",
131 | " mat2 rot2=mat2(cos(a2),sin(a2),-sin(a2),cos(a2));\n",
132 | " dir.xz*=rot1;\n",
133 | " dir.xy*=rot2;\n",
134 | " vec3 from=vec3(1.,.5,0.5);\n",
135 | " from+=vec3(time*2.,time,-2.);\n",
136 | " from.xz*=rot1;\n",
137 | " from.xy*=rot2;\n",
138 | "\n",
139 | " //volumetric rendering\n",
140 | " float s=0.1,fade=1.;\n",
141 | " vec3 v=vec3(0.);\n",
142 | " for (int r=0; r6) fade*=1.-dm; // dark matter, don't render near\n",
154 | " //v+=vec3(dm,dm*.5,0.);\n",
155 | " v+=fade;\n",
156 | " v+=vec3(s,s*s,s*s*s*s)*a*brightness*fade; // coloring based on distance\n",
157 | " fade*=distfading; // distance fading\n",
158 | " s+=stepsize;\n",
159 | " }\n",
160 | " v=mix(vec3(length(v)),v,saturation); //color adjust\n",
161 | " fragColor = vec4(v*.01,1.);\t\n",
162 | "\n",
163 | " }\n",
164 | " \n",
165 | "\"\"\", 533, 300)"
166 | ]
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": null,
171 | "metadata": {},
172 | "outputs": [],
173 | "source": []
174 | },
175 | {
176 | "cell_type": "code",
177 | "execution_count": 7,
178 | "metadata": {},
179 | "outputs": [],
180 | "source": [
181 | "l0 = Label('Star Nest by Pablo Roman Andrioli', x=10, y=10, color='cyan')"
182 | ]
183 | },
184 | {
185 | "cell_type": "code",
186 | "execution_count": null,
187 | "metadata": {},
188 | "outputs": [],
189 | "source": []
190 | },
191 | {
192 | "cell_type": "code",
193 | "execution_count": 8,
194 | "metadata": {},
195 | "outputs": [],
196 | "source": [
197 | "@app.event\n",
198 | "def render(ct, dt):\n",
199 | " \n",
200 | " st.render(ct, dt)\n",
201 | " l0.render()"
202 | ]
203 | },
204 | {
205 | "cell_type": "code",
206 | "execution_count": null,
207 | "metadata": {},
208 | "outputs": [],
209 | "source": []
210 | },
211 | {
212 | "cell_type": "code",
213 | "execution_count": 9,
214 | "metadata": {},
215 | "outputs": [],
216 | "source": [
217 | "sample = Sample('../docs/_static/audio/tb303.5.ogg', loop=True, amp=8.)\n",
218 | "sample.play()"
219 | ]
220 | },
221 | {
222 | "cell_type": "code",
223 | "execution_count": null,
224 | "metadata": {},
225 | "outputs": [],
226 | "source": []
227 | },
228 | {
229 | "cell_type": "code",
230 | "execution_count": 10,
231 | "metadata": {},
232 | "outputs": [
233 | {
234 | "data": {
235 | "application/vnd.jupyter.widget-view+json": {
236 | "model_id": "c9775505c8a54cb0b3e9034fe630ad70",
237 | "version_major": 2,
238 | "version_minor": 0
239 | },
240 | "text/plain": [
241 | "Image(value=b'\\xff\\xd8\\xff\\xe0\\x00\\x10JFIF\\x00\\x01\\x01\\x00\\x00\\x01\\x00\\x01\\x00\\x00\\xff\\xdb\\x00C\\x00\\x08\\x06\\x0…"
242 | ]
243 | },
244 | "metadata": {},
245 | "output_type": "display_data"
246 | }
247 | ],
248 | "source": [
249 | "app.run()"
250 | ]
251 | },
252 | {
253 | "cell_type": "code",
254 | "execution_count": null,
255 | "metadata": {},
256 | "outputs": [],
257 | "source": []
258 | }
259 | ],
260 | "metadata": {
261 | "kernelspec": {
262 | "display_name": "Python 3",
263 | "language": "python",
264 | "name": "python3"
265 | },
266 | "language_info": {
267 | "codemirror_mode": {
268 | "name": "ipython",
269 | "version": 3
270 | },
271 | "file_extension": ".py",
272 | "mimetype": "text/x-python",
273 | "name": "python",
274 | "nbconvert_exporter": "python",
275 | "pygments_lexer": "ipython3",
276 | "version": "3.8.5"
277 | }
278 | },
279 | "nbformat": 4,
280 | "nbformat_minor": 2
281 | }
282 |
--------------------------------------------------------------------------------
/examples/fonts/FreeLicense.txt:
--------------------------------------------------------------------------------
1 | KREATIVE SOFTWARE RELAY FONTS FREE USE LICENSE
2 | version 1.2f
3 |
4 | Permission is hereby granted, free of charge, to any person or entity (the "User") obtaining a copy of the included font files (the "Software") produced by Kreative Software, to utilize, display, embed, or redistribute the Software, subject to the following conditions:
5 |
6 | 1. The User may not sell copies of the Software for a fee.
7 |
8 | 1a. The User may give away copies of the Software free of charge provided this license and any documentation is included verbatim and credit is given to Kreative Korporation or Kreative Software.
9 |
10 | 2. The User may not modify, reverse-engineer, or create any derivative works of the Software.
11 |
12 | 3. Any Software carrying the following font names or variations thereof is not covered by this license and may not be used under the terms of this license: Jewel Hill, Miss Diode n Friends, This is Beckie's font!
13 |
14 | 3a. Any Software carrying a font name ending with the string "Pro CE" is not covered by this license and may not be used under the terms of this license.
15 |
16 | 4. This license becomes null and void if any of the above conditions are not met.
17 |
18 | 5. Kreative Software reserves the right to change this license at any time without notice.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE SOFTWARE OR FROM OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/examples/fonts/PetMe64.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/fonts/PetMe64.ttf
--------------------------------------------------------------------------------
/examples/images/alien.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/images/alien.png
--------------------------------------------------------------------------------
/examples/images/keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/images/keyboard.png
--------------------------------------------------------------------------------
/examples/images/moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/images/moon.png
--------------------------------------------------------------------------------
/examples/images/ship1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/images/ship1.png
--------------------------------------------------------------------------------
/examples/images/ship2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/images/ship2.png
--------------------------------------------------------------------------------
/examples/images/stars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/images/stars.png
--------------------------------------------------------------------------------
/examples/images/yellow-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/images/yellow-circle.png
--------------------------------------------------------------------------------
/examples/lego_3d.py:
--------------------------------------------------------------------------------
1 | """
2 | examples/lego-3d.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import logging
29 | import random
30 | import struct
31 | import time
32 | import glm
33 | import sys
34 | import os
35 |
36 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
37 |
38 | from jupylet.label import Label
39 | from jupylet.app import App
40 | from jupylet.state import State
41 | from jupylet.loader import load_blender_gltf
42 |
43 |
44 | logger = logging.getLogger()
45 |
46 |
47 | app = App(768, 512)
48 |
49 | scene = load_blender_gltf('./scenes/lego/lego.gltf')
50 | scene.shadows = True
51 |
52 | camera = scene.cameras['Camera']
53 |
54 | brick = scene.meshes['brick.green']
55 |
56 |
57 | state = State(
58 |
59 | capslock = False,
60 | shift = False,
61 | alt = False,
62 |
63 | up = False,
64 | down = False,
65 | right = False,
66 | left = False,
67 |
68 | key_w = False,
69 | key_s = False,
70 | key_a = False,
71 | key_d = False,
72 |
73 | lv = glm.vec3(0),
74 | av = glm.vec3(0),
75 | )
76 |
77 |
78 | @app.event
79 | def key_event(key, action, modifiers):
80 | logger.info('Enter key_event(key=%r, action=%r, modifiers=%r).', key, action, modifiers)
81 |
82 | keys = app.window.keys
83 |
84 | value = action != keys.ACTION_RELEASE
85 |
86 | if key == keys.CAPS_LOCK and value:
87 | state.capslock = not state.capslock
88 |
89 | state.alt = modifiers.alt
90 | state.shift = modifiers.shift
91 |
92 | if key == keys.SPACE:
93 | state.lv *= 0.
94 | state.av *= 0.
95 |
96 | if key == keys.UP:
97 | state.up = value
98 |
99 | if key == keys.DOWN:
100 | state.down = value
101 |
102 | if key == keys.LEFT:
103 | state.left = value
104 |
105 | if key == keys.RIGHT:
106 | state.right = value
107 |
108 | if key == keys.W:
109 | state.key_w = value
110 |
111 | if key == keys.S:
112 | state.key_s = value
113 |
114 | if key == keys.A:
115 | state.key_a = value
116 |
117 | if key == keys.D:
118 | state.key_d = value
119 |
120 |
121 | obj = brick if state.capslock else camera
122 |
123 | linear_acceleration = 1 / 2
124 | angular_acceleration = 1 / 24
125 |
126 |
127 | @app.run_me_every(1/48)
128 | def move_object(ct, dt):
129 |
130 | global obj
131 |
132 | obj = brick if state.capslock else camera
133 | sign = -1 if obj is camera else 1
134 |
135 | if state.right and state.shift:
136 | state.av.z += angular_acceleration * sign
137 |
138 | if state.right and not state.shift:
139 | state.av.y -= angular_acceleration
140 |
141 | if state.left and state.shift:
142 | state.av.z -= angular_acceleration * sign
143 |
144 | if state.left and not state.shift:
145 | state.av.y += angular_acceleration
146 |
147 | if state.up:
148 | state.av.x -= angular_acceleration
149 |
150 | if state.down:
151 | state.av.x += angular_acceleration
152 |
153 | if state.key_w and state.alt:
154 | state.lv.y += linear_acceleration
155 |
156 | if state.key_w and not state.alt:
157 | state.lv.z += linear_acceleration * sign
158 |
159 | if state.key_s and state.alt:
160 | state.lv.y -= linear_acceleration
161 |
162 | if state.key_s and not state.alt:
163 | state.lv.z -= linear_acceleration * sign
164 |
165 | if state.key_a:
166 | state.lv.x += linear_acceleration * sign
167 |
168 | if state.key_d:
169 | state.lv.x -= linear_acceleration * sign
170 |
171 | state.lv = glm.clamp(state.lv, -64, 64)
172 | state.av = glm.clamp(state.av, -64, 64)
173 |
174 | obj.move_local(dt * state.lv)
175 |
176 | obj.rotate_local(dt * state.av.x, (1, 0, 0))
177 | obj.rotate_local(dt * state.av.y, (0, 1, 0))
178 | obj.rotate_local(dt * state.av.z, (0, 0, 1))
179 |
180 | state.lv *= 0.67 ** dt
181 | state.av *= 0.67 ** dt
182 |
183 |
184 | label0 = Label('Hello World!', color='white', font_size=12, x=10, y=74)
185 | label1 = Label('Hello World!', color='white', font_size=12, x=10, y=52)
186 | label2 = Label('Hello World!', color='white', font_size=12, x=10, y=30)
187 | label3 = Label('Hello World!', color='white', font_size=12, x=10, y=8)
188 |
189 | hello_world = Label('hello, world 3D!', color='cyan', font_size=24, x=575, y=10)
190 |
191 |
192 | @app.event
193 | def render(ct, dt):
194 |
195 | app.window.clear()
196 |
197 | scene.draw()
198 |
199 | label0.text = 'time to draw - %.2f ms' % (1000 * app._time2draw_rm)
200 | label1.text = 'up - %r' % obj.up
201 | label2.text = 'front - %r' % obj.front
202 | label3.text = 'position - %r' % obj.position
203 |
204 | label0.draw()
205 | label1.draw()
206 | label2.draw()
207 | label3.draw()
208 |
209 | hello_world.draw()
210 |
211 |
212 | if __name__ == '__main__':
213 | app.run()
214 |
215 |
--------------------------------------------------------------------------------
/examples/piano.py:
--------------------------------------------------------------------------------
1 | """
2 | examples/sounds_demo.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import sys
29 | import os
30 |
31 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
32 |
33 | import jupylet.color
34 |
35 | from jupylet.app import App
36 | from jupylet.state import State
37 | from jupylet.label import Label
38 | from jupylet.sprite import Sprite
39 | from jupylet.shadertoy import Shadertoy, get_shadertoy_audio
40 |
41 | from jupylet.audio.bundle import *
42 |
43 | import numpy as np
44 |
45 |
46 | app = App(width=512, height=420, quality=100)#, log_level=logging.INFO)
47 |
48 |
49 | #
50 | # Default oscilloscope shader:
51 | # The code in the following cell is of a simple shadertoy shader that
52 | # displays an audio oscilloscope. Shadertoy (http://shadertoy.com/) are
53 | # an easy way to create graphic effects by programming the GPU directly:
54 | #
55 | st0 = Shadertoy("""
56 |
57 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
58 | {
59 | // Normalized pixel coordinates (from 0 to 1)
60 | vec2 uv = fragCoord / iResolution.xy;
61 |
62 | // Time varying pixel color
63 | vec3 col = 0.2 + 0.2 * cos(iTime + uv.xyx + vec3(0, 2, 4));
64 |
65 | float amp = texture(iChannel0, vec2(uv.x, 1.)).r;
66 |
67 | vec3 sig = vec3(0.00033 / max(pow(amp - uv.y, 2.), 1e-6));
68 |
69 | sig *= vec3(.5, .5, 4.) / 2.;
70 |
71 | col += sig;
72 |
73 | // Output to screen
74 | fragColor = vec4(col,1.0);
75 | }
76 |
77 | """, 512, 256, 0, 420, 0, 'left', 'top')
78 |
79 |
80 | #
81 | # Audio visualization shader by Alban Fichet:
82 | # The code in the following cell is of a beautiful shadertoy shader by
83 | # graphics expert and researcher Alban Fichet (https://afichet.github.io/)
84 | # who kindly allowed its inclusion here under CC BY 4.0 license:
85 | #
86 | ba1 = Shadertoy("""
87 |
88 | vec3 hue2rgb(in float h) {
89 | vec3 k = mod(vec3(5., 3., 1.) + vec3(h*360./60.), vec3(6.));
90 | return vec3(1.) - clamp(min(k, vec3(4.) - k), vec3(0.), vec3(1.));
91 | }
92 |
93 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
94 | {
95 | vec2 uv = fragCoord/iResolution.xy;
96 |
97 | float aspect = iResolution.x / iResolution.y;
98 | float blurr = 0.3;
99 | float sharpen = 1.7;
100 |
101 | vec2 maxWindow = vec2(3., 3./aspect);
102 | uv = mix(-maxWindow, maxWindow, uv);
103 |
104 | float r = dot(uv, uv);
105 | float theta = atan(uv.y, uv.x) + 3.14;
106 |
107 | float t = abs(2.*theta / (2.*3.14) - 1.);
108 |
109 | float signal = 2.0*texture(iChannel0,vec2(t,1.)).x;
110 | float ampl = 2.0*texture(iChannel0,vec2(0.8,.25)).x;
111 |
112 | float v = 1. - pow(smoothstep(0., blurr, abs(r - signal)), 0.02);
113 | float hue = pow(fract(abs(sin(theta/2.) * ampl)), sharpen);
114 |
115 | fragColor = vec4(v * hue2rgb(fract(hue + iTime/10.)), 1.0);
116 | }
117 |
118 | """)
119 |
120 | bb1 = Shadertoy("""
121 |
122 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
123 | {
124 | vec2 uv = fragCoord/iResolution.xy;
125 |
126 | vec2 center = vec2(.5 + 0.15*sin(iTime));
127 | float zoom = 1.02;
128 |
129 | vec4 prevParams = texture(iChannel0, (uv - center)/zoom + center);
130 | vec4 bB = texture(iChannel1, uv);
131 |
132 | fragColor = clamp(mix(prevParams, 4 * bB, 0.1), 0., 1.) ;
133 | }
134 |
135 | """)
136 |
137 | bb1.set_channel(0, bb1)
138 | bb1.set_channel(1, ba1)
139 |
140 | st1 = Shadertoy("""
141 |
142 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
143 | {
144 | vec3 bA = texelFetch(iChannel0, ivec2(fragCoord), 0).rgb;
145 | vec3 bB = texelFetch(iChannel1, ivec2(fragCoord), 0).rgb;
146 |
147 | vec3 col_rgb = bB;
148 |
149 | col_rgb = col_rgb*exp2(3.5);
150 |
151 | fragColor = vec4(pow(col_rgb, vec3(1./2.2)), 1.);
152 | }
153 |
154 | """, 512, 256, 0, 420, 0, 'left', 'top')
155 |
156 |
157 | st1.set_channel(0, ba1)
158 | st1.set_channel(1, bb1)
159 |
160 | keyboard_layout = Sprite('images/keyboard.png', x=256, y=82, scale=0.5)
161 | label0 = Label('amp: %.2f' % get_master_volume(), anchor_x='right', x=app.width - 10, y=174)
162 | label1 = Label('use ↑ ↓ to control volume', x=10, y=194)
163 | label2 = Label('tap SPACE to change shader', x=10, y=174)
164 |
165 | state = State(
166 |
167 | up = False,
168 | down = False,
169 | shader = 0,
170 | )
171 |
172 | keys = app.window.keys
173 |
174 | keyboard = {
175 |
176 | keys.Z: note.C,
177 | keys.S: note.Cs,
178 | keys.X: note.D,
179 | keys.D: note.Ds,
180 | keys.C: note.E,
181 | keys.V: note.F,
182 | keys.G: note.Fs,
183 | keys.B: note.G,
184 | keys.H: note.Gs,
185 | keys.N: note.A,
186 | keys.J: note.As,
187 | keys.M: note.B,
188 |
189 | keys.Q: note.C5,
190 | 50: note.Cs5,
191 | keys.W: note.D5,
192 | 51: note.Ds5,
193 | keys.E: note.E5,
194 | keys.R: note.F5,
195 | 53: note.Fs5,
196 | keys.T: note.G5,
197 | 54: note.Gs5,
198 | keys.Y: note.A5,
199 | 55: note.As5,
200 | keys.U: note.B5,
201 |
202 | keys.I: note.C6,
203 | 57: note.Cs6,
204 | keys.O: note.D6,
205 | 48: note.Ds6,
206 | keys.P: note.E6,
207 | }
208 |
209 |
210 | _keyd = {}
211 |
212 | @app.event
213 | def key_event(key, action, modifiers):
214 |
215 | keys = app.window.keys
216 | value = action == keys.ACTION_PRESS
217 |
218 | if key == keys.UP:
219 | state.up = value
220 |
221 | if key == keys.DOWN:
222 | state.down = value
223 |
224 | if key == keys.SPACE and action == keys.ACTION_PRESS:
225 | state.shader = 1 - state.shader
226 |
227 | if action == keys.ACTION_PRESS and key in keyboard:
228 | assert key not in _keyd
229 | _keyd[key] = tb303.play_poly(note=keyboard[key])
230 |
231 | if action == keys.ACTION_RELEASE and key in keyboard:
232 | _keyd.pop(key).play_release()
233 |
234 |
235 | @app.run_me_every(1/24)
236 | def modify_volume(ct, dt):
237 |
238 | s = 2 ** dt
239 | amp = get_master_volume()
240 |
241 | if state.up:
242 | amp *= s
243 | set_master_volume(amp)
244 | label0.text = 'amp: %.2f' % amp
245 |
246 | if state.down:
247 | amp /= s
248 | set_master_volume(amp)
249 | label0.text = 'amp: %.2f' % amp
250 |
251 |
252 | @app.event
253 | def render(ct, dt):
254 |
255 | app.window.clear(color='#555')
256 |
257 | keyboard_layout.draw()
258 |
259 | if state.shader == 0:
260 | st0.set_channel(0, *get_shadertoy_audio(amp=5))
261 | st0.render(ct, dt)
262 | else:
263 | ba1.set_channel(0, *get_shadertoy_audio(amp=5))
264 | st1.render(ct, dt)
265 |
266 | label0.draw()
267 | label1.draw()
268 | label2.draw()
269 |
270 |
271 | app.set_midi_sound(tb303)
272 |
273 | set_latency('lowest')
274 | set_effects(ConvolutionReverb('./sounds/impulses/MaesHowe.flac'))
275 |
276 | xylo = Sample('sounds/VCSL/Xylophone/Xylophone - Medium Mallets.sfz')
277 | xylo.amp = 8
278 |
279 |
280 | @app.sonic_live_loop
281 | async def loop0():
282 |
283 | use(tb303, duration=2, amp=0.15)
284 |
285 | play(note.C2)
286 | await sleep(3)
287 |
288 | play(note.E2)
289 | await sleep(3)
290 |
291 | play(note.C2)
292 | await sleep(6)
293 |
294 |
295 | @app.sonic_live_loop
296 | async def loop1():
297 |
298 | use(xylo, amp=8)
299 |
300 | play(note.C5)
301 | await sleep(1)
302 |
303 | play(note.E5)
304 | await sleep(1)
305 |
306 | play(note.G5)
307 | await sleep(1)
308 |
309 |
310 | if __name__ == '__main__':
311 | app.run(1/30)
312 |
313 |
--------------------------------------------------------------------------------
/examples/pong-start.state:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/pong-start.state
--------------------------------------------------------------------------------
/examples/scenes/lego/lego.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/lego/lego.bin
--------------------------------------------------------------------------------
/examples/scenes/lego/lego.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/lego/lego.blend
--------------------------------------------------------------------------------
/examples/scenes/moon/TexturesCom_Leather_Plain_1K_albedo_blue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/TexturesCom_Leather_Plain_1K_albedo_blue.jpg
--------------------------------------------------------------------------------
/examples/scenes/moon/TexturesCom_Leather_Plain_1K_normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/TexturesCom_Leather_Plain_1K_normal.jpg
--------------------------------------------------------------------------------
/examples/scenes/moon/TexturesCom_Leather_Plain_1K_roughness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/TexturesCom_Leather_Plain_1K_roughness.png
--------------------------------------------------------------------------------
/examples/scenes/moon/alien-moon.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/alien-moon.bin
--------------------------------------------------------------------------------
/examples/scenes/moon/alien-moon.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/alien-moon.blend
--------------------------------------------------------------------------------
/examples/scenes/moon/eye.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/eye.jpg
--------------------------------------------------------------------------------
/examples/scenes/moon/lroc_color_poles_4k.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/lroc_color_poles_4k.jpg
--------------------------------------------------------------------------------
/examples/scenes/moon/moon-normal-map.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/moon-normal-map.jpg
--------------------------------------------------------------------------------
/examples/scenes/moon/nebula/nebulaBK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/nebula/nebulaBK.png
--------------------------------------------------------------------------------
/examples/scenes/moon/nebula/nebulaDN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/nebula/nebulaDN.png
--------------------------------------------------------------------------------
/examples/scenes/moon/nebula/nebulaFT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/nebula/nebulaFT.png
--------------------------------------------------------------------------------
/examples/scenes/moon/nebula/nebulaLF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/nebula/nebulaLF.png
--------------------------------------------------------------------------------
/examples/scenes/moon/nebula/nebulaRT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/nebula/nebulaRT.png
--------------------------------------------------------------------------------
/examples/scenes/moon/nebula/nebulaUP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/scenes/moon/nebula/nebulaUP.png
--------------------------------------------------------------------------------
/examples/shadertoy_demo.py:
--------------------------------------------------------------------------------
1 | """
2 | examples/shadertoy_demo.py
3 |
4 | This script demonstrates how to display shadertoys. Check out the
5 | documentation for more information:
6 | https://jupylet.readthedocs.io/en/latest/programmers_reference_guide/graphics-3d.html#shadertoys
7 |
8 | The code displays the beautiful Star Nest Shadertoy by Pablo Roman Andrioli
9 | https://www.shadertoy.com/view/XlfGRj.
10 | """
11 |
12 |
13 | import sys
14 | import os
15 |
16 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
17 |
18 | from jupylet.app import App
19 | from jupylet.label import Label
20 | from jupylet.shadertoy import Shadertoy
21 |
22 | from jupylet.audio.bundle import *
23 |
24 |
25 | app = App(width=533, height=300)
26 |
27 |
28 | st = Shadertoy("""
29 |
30 | // Star Nest by Pablo Roman Andrioli
31 |
32 | // This content is under the MIT License.
33 |
34 | #define iterations 17
35 | #define formuparam 0.53
36 |
37 | #define volsteps 20
38 | #define stepsize 0.1
39 |
40 | #define zoom 0.800
41 | #define tile 0.850
42 | #define speed 0.010
43 |
44 | #define brightness 0.0015
45 | #define darkmatter 0.300
46 | #define distfading 0.730
47 | #define saturation 0.850
48 |
49 |
50 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
51 | {
52 | //get coords and direction
53 | vec2 uv=fragCoord.xy/iResolution.xy-.5;
54 | uv.y*=iResolution.y/iResolution.x;
55 | vec3 dir=vec3(uv*zoom,1.);
56 | float time=iTime*speed+.25;
57 |
58 | //mouse rotation
59 | float a1=.5+iMouse.x/iResolution.x*2.;
60 | float a2=.8+iMouse.y/iResolution.y*2.;
61 | mat2 rot1=mat2(cos(a1),sin(a1),-sin(a1),cos(a1));
62 | mat2 rot2=mat2(cos(a2),sin(a2),-sin(a2),cos(a2));
63 | dir.xz*=rot1;
64 | dir.xy*=rot2;
65 | vec3 from=vec3(1.,.5,0.5);
66 | from+=vec3(time*2.,time,-2.);
67 | from.xz*=rot1;
68 | from.xy*=rot2;
69 |
70 | //volumetric rendering
71 | float s=0.1,fade=1.;
72 | vec3 v=vec3(0.);
73 | for (int r=0; r6) fade*=1.-dm; // dark matter, don't render near
85 | //v+=vec3(dm,dm*.5,0.);
86 | v+=fade;
87 | v+=vec3(s,s*s,s*s*s*s)*a*brightness*fade; // coloring based on distance
88 | fade*=distfading; // distance fading
89 | s+=stepsize;
90 | }
91 | v=mix(vec3(length(v)),v,saturation); //color adjust
92 | fragColor = vec4(v*.01,1.);
93 |
94 | }
95 |
96 | """, 533, 300)#, -1066, -600)#, 0, 'center', 'center')
97 |
98 |
99 | l0 = Label('Star Nest by Pablo Roman Andrioli', x=10, y=10, color='cyan')
100 |
101 |
102 | @app.event
103 | def render(ct, dt):
104 |
105 | st.render(ct, dt)
106 | l0.render()
107 |
108 |
109 | sample = Sample('../docs/_static/audio/tb303.5.ogg', loop=True, amp=8.)
110 | sample.play()
111 |
112 |
113 | if __name__ == '__main__':
114 | app.run()
115 |
116 |
--------------------------------------------------------------------------------
/examples/sounds/VCSL/README.md:
--------------------------------------------------------------------------------
1 | # VCSL
2 | 
3 |
4 | The Versilian Community Sample Library is an open CC0 general-purpose sample library created by Versilian Studios LLC for the purpose of introducing a set of quality, publicly available samples suitable for use in software and media of all kinds. This library is intended to be a broader expansion to the VSCO 2 CE sample set.
5 |
6 | This collection is under a Creative Commons 0 license. Essentially it's Public Domain- you can do whatever you want with these sounds (even make commercial software), no royalties, no credit, no special terms.
7 |
8 | The easiest way to keep up to date is to set up a local copy of the repository with Github Desktop, then just 'Fetch origin' whenever something new comes out. Install Github Desktop, then back on the VCSL main branch page click 'Clone or Download' then 'Open in Desktop'.
9 |
10 | Want to stay updated? Join the mailing list and we'll send you an e-mail every time the project is updated-
11 | http://www.versilstudios.net/sendy/subscription?f=b763a892L4k9FXaEnJ3glhJUq3yyF71hesBFROqKTz08slZriGNWCYyV7OLLLPCTd8Dn
12 |
13 | # Design Philosophy
14 | The selection of samples for VCSL is designed specifically to suit lightweight sample library creation. A greater emphasis is placed on pitch fidelity (with most being wholetone sampled if possible) and consistency, but less on more 'professional' features such as many round robins or velocity layers, except where warranted (e.g. percussion). As such, most articulations only have a single round robin and two to three velocity layers, although like all things in life, there are exceptions to that. On average, this means each instrument weighs about 20-75 MB in raw .wav format, a small but comfortable size for adequate reproduction.
15 |
16 | This goal of this set is to provide a consistently-recorded and convenient starting place for factory libraries, sound designers, and hobbyist developers to work from.
17 |
18 | # Inclusion of Previous Works
19 | This set shall include the bulk of the VSCO 2 CE sample set, including some reworked and re-curated selections to better fit the design philosophy. It also shall include many instruments not featured in the CE sample set, but found in other products or the larger VSCO 2 Pro sample set. Lastly, new recordings will be produced ongoing throughout the next few years to continually add and update content in this sample set. Content added to the set will not be removed; new content will simply be added on in the form of new instrument options.
20 |
21 | # 3rd Party Contributions
22 | 3rd party developers are welcome to make additions to the sample set. The following standards are highly recommended:
23 | - Samples shall be named in a human-readable format. The recommended syntax is- `[Instrument]_[Articulation]_[Mic]_vl[Velocity]_rr[RR].wav.`
24 | - Samples shall be made available at 44.1 or 48 kHz sample rate, and either 16 or 24-bit depth, as uncompressed .wav files.
25 | - Samples shall be recorded in stereo if possible, unless (1) idiomatic (e.g. harmonicas, solo vox) or (2) impractical to do so. Most of the sample set is recorded with spaced pair or NOS, but other arrays are acceptable so long as the stereo image is reasonable.
26 | - Samples shall preferably be recorded in a clean, 'mid-close' position, typically 0.5-3 meters from the performer(s), in an acoustically neutral space with as little background noise as possible. These recordings shall not be processed with the exception of (1) tuning, (2) mild eq'ing to fix microphone problems, and (3) noise reduction (if necessary).
27 | - Samples shall be organized in folders, one per instrument, preferably with sub-folders, one per articulation, if there are many samples.
28 | - Any metadata relating to the samples should be included (brand/model, performer, mics, space) if possible, but not necessary.
29 |
30 | Contributions may be made by creating a fork, and then requesting that fork to be merged back into the main set. In addition, forks that go off and do their 'own thing' are welcome as well (such as a 3rd party SFZ conversion).
31 |
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone - Medium Mallets.sfz:
--------------------------------------------------------------------------------
1 | // Generation Options: --notuning --volume 5 --release 15
2 |
3 |
4 | ampeg_attack=0.004000
5 | ampeg_release=15.000000
6 |
7 |
8 | lovel=0
9 | hivel=83
10 | sample=Xylophone/Medium Mallets/Xylo_Medium_G3_pp_01_far.ogg
11 | pitch_keycenter=55
12 | lokey=55
13 | hikey=57
14 | volume=22.810545
15 |
16 |
17 | lovel=84
18 | hivel=127
19 | sample=Xylophone/Medium Mallets/Xylo_Medium_G3_ff_01_far.ogg
20 | pitch_keycenter=55
21 | lokey=55
22 | hikey=57
23 | volume=1.425835
24 |
25 |
26 | lovel=0
27 | hivel=83
28 | sample=Xylophone/Medium Mallets/Xylo_Medium_C4_pp_01_far.ogg
29 | pitch_keycenter=60
30 | lokey=58
31 | hikey=63
32 | volume=12.381368
33 |
34 |
35 | lovel=84
36 | hivel=127
37 | sample=Xylophone/Medium Mallets/Xylo_Medium_C4_ff_01_far.ogg
38 | pitch_keycenter=60
39 | lokey=58
40 | hikey=63
41 | volume=1.863334
42 |
43 |
44 | lovel=0
45 | hivel=83
46 | sample=Xylophone/Medium Mallets/Xylo_Medium_G4_pp_01_far.ogg
47 | pitch_keycenter=67
48 | lokey=64
49 | hikey=69
50 | volume=10.140772
51 |
52 |
53 | lovel=84
54 | hivel=127
55 | sample=Xylophone/Medium Mallets/Xylo_Medium_G4_ff_01_far.ogg
56 | pitch_keycenter=67
57 | lokey=64
58 | hikey=69
59 | volume=-1.767685
60 |
61 |
62 | lovel=0
63 | hivel=83
64 | sample=Xylophone/Medium Mallets/Xylo_Medium_C5_pp_01_far.ogg
65 | pitch_keycenter=72
66 | lokey=70
67 | hikey=75
68 | volume=13.198534
69 |
70 |
71 | lovel=84
72 | hivel=127
73 | sample=Xylophone/Medium Mallets/Xylo_Medium_C5_ff_01_far.ogg
74 | pitch_keycenter=72
75 | lokey=70
76 | hikey=75
77 | volume=2
78 |
79 |
80 | lovel=0
81 | hivel=83
82 | sample=Xylophone/Medium Mallets/Xylo_Medium_G5_pp_01_far.ogg
83 | pitch_keycenter=79
84 | lokey=76
85 | hikey=81
86 | volume=15.197086
87 |
88 |
89 | lovel=84
90 | hivel=127
91 | sample=Xylophone/Medium Mallets/Xylo_Medium_G5_ff_01_far.ogg
92 | pitch_keycenter=79
93 | lokey=76
94 | hikey=81
95 | volume=1.039045
96 |
97 |
98 | lovel=0
99 | hivel=83
100 | sample=Xylophone/Medium Mallets/Xylo_Medium_C6_pp_01_far.ogg
101 | pitch_keycenter=84
102 | lokey=82
103 | hikey=87
104 | volume=15.817703
105 |
106 |
107 | lovel=84
108 | hivel=127
109 | sample=Xylophone/Medium Mallets/Xylo_Medium_C6_ff_01_far.ogg
110 | pitch_keycenter=84
111 | lokey=82
112 | hikey=87
113 | volume=3.136201
114 |
115 |
116 | lovel=0
117 | hivel=83
118 | sample=Xylophone/Medium Mallets/Xylo_Medium_G6_pp_01_far.ogg
119 | pitch_keycenter=91
120 | lokey=88
121 | hikey=93
122 | volume=21.807102
123 |
124 |
125 | lovel=84
126 | hivel=127
127 | sample=Xylophone/Medium Mallets/Xylo_Medium_G6_ff_01_far.ogg
128 | pitch_keycenter=91
129 | lokey=88
130 | hikey=93
131 | volume=2.679822
132 |
133 |
134 | lovel=0
135 | hivel=83
136 | sample=Xylophone/Medium Mallets/Xylo_Medium_C7_pp_01_far.ogg
137 | pitch_keycenter=96
138 | lokey=94
139 | hikey=97
140 | volume=16.578977
141 |
142 |
143 | lovel=84
144 | hivel=127
145 | sample=Xylophone/Medium Mallets/Xylo_Medium_C7_ff_01_far.ogg
146 | pitch_keycenter=96
147 | lokey=94
148 | hikey=97
149 | volume=4.621578
150 |
151 |
152 |
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C4_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C4_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C4_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C4_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C5_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C5_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C5_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C5_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C6_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C6_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C6_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C6_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C7_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C7_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C7_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_C7_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G3_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G3_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G3_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G3_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G4_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G4_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G4_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G4_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G5_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G5_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G5_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G5_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G6_ff_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G6_ff_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G6_pp_01_far.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/VCSL/Xylophone/Xylophone/Medium Mallets/Xylo_Medium_G6_pp_01_far.ogg
--------------------------------------------------------------------------------
/examples/sounds/pong-blip.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/sounds/pong-blip.wav
--------------------------------------------------------------------------------
/examples/spaceship.py:
--------------------------------------------------------------------------------
1 | """
2 | examples/spaceship.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import logging
29 | import math
30 | import sys
31 | import os
32 |
33 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
34 |
35 | from jupylet.sprite import Sprite
36 | from jupylet.label import Label
37 | from jupylet.app import App
38 |
39 |
40 | logger = logging.getLogger()
41 |
42 |
43 | app = App()
44 |
45 |
46 | stars = Sprite('images/stars.png', scale=2.5)
47 | alien = Sprite('images/alien.png', scale=0.5)
48 | ship = Sprite('images/ship1.png', x=app.width/2, y=app.height/2, scale=0.5)
49 | moon = Sprite('images/moon.png', x=app.width-70, y=app.height-70, scale=0.5)
50 |
51 | circle = Sprite('images/yellow-circle.png', width=184)
52 | circle.opacity = 0.
53 |
54 | label = Label('hello, world', color='cyan', font_size=32, x=10, y=10)
55 |
56 |
57 | @app.event
58 | def mouse_position_event(x, y, dx, dy):
59 | logger.info('Enter mouse_position_event(%r, %r, %r, %r).', x, y, dx, dy)
60 |
61 | alien.x = x
62 | alien.y = y
63 |
64 | circle.x = x
65 | circle.y = y
66 |
67 |
68 | vx = 0
69 | vy = 0
70 |
71 | up = 0
72 | left = 0
73 | right = 0
74 |
75 |
76 | @app.run_me_every(1/60)
77 | def update_ship(ct, dt):
78 |
79 | global vx, vy
80 |
81 | if left:
82 | ship.angle += 128 * dt
83 |
84 | if right:
85 | ship.angle -= 128 * dt
86 |
87 | if up:
88 | vx += 3 * math.cos(math.radians(90 + ship.angle))
89 | vy += 3 * math.sin(math.radians(90 + ship.angle))
90 |
91 | ship.x += vx * dt
92 | ship.y += vy * dt
93 |
94 | ship.wrap_position(app.width, app.height)
95 |
96 | if len(ship.collisions_with(alien)) > 0:
97 | circle.opacity = 0.5
98 | else:
99 | circle.opacity = 0.0
100 |
101 |
102 | @app.run_me_every(1/60)
103 | def rotate(ct, dt):
104 |
105 | alien.angle += 64 * dt
106 |
107 |
108 | @app.event
109 | def key_event(key, action, modifiers):
110 | logger.info('Enter key_event(key=%r, action=%r, modifiers=%r).', key, action, modifiers)
111 |
112 | global up, left, right
113 |
114 | keys = app.window.keys
115 |
116 | if action == keys.ACTION_PRESS:
117 |
118 | if key == keys.UP:
119 | ship.image = 'images/ship2.png'
120 | up = True
121 |
122 | if key == keys.LEFT:
123 | left = True
124 |
125 | if key == keys.RIGHT:
126 | right = True
127 |
128 | if action == keys.ACTION_RELEASE:
129 |
130 | if key == keys.UP:
131 | ship.image = 'images/ship1.png'
132 | up = False
133 |
134 | if key == keys.LEFT:
135 | left = False
136 |
137 | if key == keys.RIGHT:
138 | right = False
139 |
140 |
141 | @app.event
142 | def render(ct, dt):
143 | #logger.debug('Enter render(%r, %r).', ct, dt)
144 |
145 | app.window.clear()
146 |
147 | stars.draw()
148 | moon.draw()
149 |
150 | circle.draw()
151 | alien.draw()
152 | ship.draw()
153 |
154 | label.draw()
155 |
156 |
157 | if __name__ == '__main__':
158 | app.run()
159 |
160 |
--------------------------------------------------------------------------------
/examples/spaceship_3d.py:
--------------------------------------------------------------------------------
1 | """
2 | examples/spaceship_3d.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import logging
29 | import random
30 | import struct
31 | import time
32 | import glm
33 | import sys
34 | import os
35 |
36 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
37 |
38 | from jupylet.label import Label
39 | from jupylet.app import App
40 | from jupylet.state import State
41 | from jupylet.model import Skybox
42 | from jupylet.loader import load_blender_gltf
43 |
44 |
45 | logger = logging.getLogger()
46 |
47 |
48 | if __name__ == '__main__':
49 | mode = 'window'
50 | else:
51 | mode = 'hidden'
52 |
53 | app = App(768, 512, mode=mode)#, log_level=logging.INFO)
54 |
55 | scene = load_blender_gltf('./scenes/moon/alien-moon.gltf')
56 |
57 | scene.skybox = Skybox('./scenes/moon/nebula/nebula*.png', intensity=2., flip_left_right=True)
58 |
59 | scene.shadows = True
60 |
61 |
62 | sun = scene.lights['Light.Sun']
63 | sun.shadowmaps_depths = [1., 0.12, 0.04, 0.015, 0.0]
64 |
65 | moon = scene.meshes['Moon']
66 | moon.shadow_bias = 0.2
67 |
68 | camera = scene.cameras['Camera']
69 |
70 |
71 | state = State(
72 |
73 | capslock = False,
74 | shift = False,
75 | alt = False,
76 |
77 | up = False,
78 | down = False,
79 | right = False,
80 | left = False,
81 |
82 | key_w = False,
83 | key_s = False,
84 | key_a = False,
85 | key_d = False,
86 |
87 | lv = glm.vec3(0),
88 | av = glm.vec3(0),
89 | )
90 |
91 |
92 | @app.event
93 | def key_event(key, action, modifiers):
94 | logger.info('Enter key_event(key=%r, action=%r, modifiers=%r).', key, action, modifiers)
95 |
96 | keys = app.window.keys
97 |
98 | value = action != keys.ACTION_RELEASE
99 |
100 | if key == keys.CAPS_LOCK and value:
101 | state.capslock = not state.capslock
102 |
103 | state.alt = modifiers.alt
104 | state.shift = modifiers.shift
105 |
106 | if key == keys.SPACE:
107 | state.lv *= 0.
108 | state.av *= 0.
109 |
110 | if key == keys.UP:
111 | state.up = value
112 |
113 | if key == keys.DOWN:
114 | state.down = value
115 |
116 | if key == keys.LEFT:
117 | state.left = value
118 |
119 | if key == keys.RIGHT:
120 | state.right = value
121 |
122 | if key == keys.W:
123 | state.key_w = value
124 |
125 | if key == keys.S:
126 | state.key_s = value
127 |
128 | if key == keys.A:
129 | state.key_a = value
130 |
131 | if key == keys.D:
132 | state.key_d = value
133 |
134 |
135 | obj = moon if state.capslock else camera
136 |
137 | linear_acceleration = 1 / 2
138 | angular_acceleration = 1 / 24
139 |
140 |
141 | @app.run_me_every(1/48)
142 | def move_object(ct, dt):
143 |
144 | global obj
145 |
146 | obj = moon if state.capslock else camera
147 | sign = -1 if obj is camera else 1
148 |
149 | if state.right and state.shift:
150 | state.av.z += angular_acceleration * sign
151 |
152 | if state.right and not state.shift:
153 | state.av.y -= angular_acceleration
154 |
155 | if state.left and state.shift:
156 | state.av.z -= angular_acceleration * sign
157 |
158 | if state.left and not state.shift:
159 | state.av.y += angular_acceleration
160 |
161 | if state.up:
162 | state.av.x -= angular_acceleration
163 |
164 | if state.down:
165 | state.av.x += angular_acceleration
166 |
167 | if state.key_w and state.alt:
168 | state.lv.y += linear_acceleration
169 |
170 | if state.key_w and not state.alt:
171 | state.lv.z += linear_acceleration * sign
172 |
173 | if state.key_s and state.alt:
174 | state.lv.y -= linear_acceleration
175 |
176 | if state.key_s and not state.alt:
177 | state.lv.z -= linear_acceleration * sign
178 |
179 | if state.key_a:
180 | state.lv.x += linear_acceleration * sign
181 |
182 | if state.key_d:
183 | state.lv.x -= linear_acceleration * sign
184 |
185 | state.lv = glm.clamp(state.lv, -64, 64)
186 | state.av = glm.clamp(state.av, -64, 64)
187 |
188 | obj.move_local(dt * state.lv)
189 |
190 | obj.rotate_local(dt * state.av.x, (1, 0, 0))
191 | obj.rotate_local(dt * state.av.y, (0, 1, 0))
192 | obj.rotate_local(dt * state.av.z, (0, 0, 1))
193 |
194 | state.lv *= 0.67 ** dt
195 | state.av *= 0.67 ** dt
196 |
197 |
198 | label0 = Label('Hello World!', color='white', font_size=14, x=10, y=74)
199 | label1 = Label('Hello World!', color='white', font_size=14, x=10, y=52)
200 | label2 = Label('Hello World!', color='white', font_size=14, x=10, y=30)
201 | label3 = Label('Hello World!', color='white', font_size=14, x=10, y=8)
202 |
203 | hello_world = Label('hello, world 3D!', color='cyan', font_size=24, x=575, y=10)
204 |
205 |
206 | @app.event
207 | def render(ct, dt):
208 |
209 | app.window.clear(blue=0.3)
210 |
211 | scene.draw()
212 |
213 | label0.text = 'time to draw - %0.3f ms' % (1000 * app._time2draw_rm)
214 | label1.text = 'up - %0.3f, %0.3f, %0.3f' % tuple(obj.up)
215 | label2.text = 'front - %0.3f, %0.3f, %0.3f' % tuple(obj.front)
216 | label3.text = 'position - %0.3f, %0.3f, %0.3f' % tuple(obj.position)
217 |
218 | label0.draw()
219 | label1.draw()
220 | label2.draw()
221 | label3.draw()
222 |
223 | hello_world.draw()
224 |
225 |
226 | @app.schedule_interval(1/30)
227 | def spin(ct, dt):
228 | scene.meshes['Alien'].rotate_local(-0.5 * dt, (0, 0, 1))
229 |
230 |
231 | if __name__ == '__main__':
232 | app.run()
233 |
234 |
--------------------------------------------------------------------------------
/examples/spectrum_analyzer.state:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/examples/spectrum_analyzer.state
--------------------------------------------------------------------------------
/jupylet/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/__init__.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import platform
29 | import sys
30 | import os
31 | import re
32 |
33 | from .env import is_remote, has_display, is_numpy_openblas, is_jupyter
34 |
35 |
36 | VERSION = '0.9.2'
37 |
38 |
39 | if platform.system() == 'Linux' and not has_display():
40 | setattr(sys, 'is_pyglet_doc_run', True)
41 |
42 |
43 | #
44 | # Workaround segmentation fault when calling np.linalg.inv() in
45 | # mutlithreaded app.
46 | #
47 | if platform.system() == 'Darwin':
48 | if 'numpy' in sys.modules and is_numpy_openblas():
49 | sys.stderr.write(
50 | 'WARNING: numpy was imported before jupylet. ' +
51 | 'On macOS you should import jupylet first to let it work around ' +
52 | 'a bug in the algebra libraries used by numpy that may cause the ' +
53 | 'program to exit.' + '\n'
54 | )
55 |
56 | os.environ['OPENBLAS_NUM_THREADS'] = '1'
57 |
58 |
59 | if platform.system() == 'Darwin':
60 | try:
61 | import moderngl
62 | moderngl.create_standalone_context().release()
63 | except NameError:
64 | if is_jupyter():
65 | raise SystemError('ERROR: Library libcxx is missing. You can install it by running: conda install libcxx')
66 | sys.stderr.write('ERROR: Library libcxx is missing. You can install it by running: conda install libcxx\n')
67 | sys.exit(0)
68 |
69 |
70 | #
71 | # Work around problem in pip install jupyter in python 3.8 as described in:
72 | # https://github.com/jupyter/notebook/issues/4980#issuecomment-600992296
73 | #
74 | if platform.system() == 'Windows' and sys.version_info >= (3, 8):
75 | if sys.argv[-2:] == ['-m', 'postinstall']:
76 | os.system(r'python %s\Scripts\pywin32_postinstall.py -install' % os.__file__.rsplit('\\', 2)[0])
77 | sys.exit(0)
78 |
79 |
80 | def download_url(url, progress=False):
81 |
82 | if not progress:
83 | r = urllib.request.urlopen(GITHUB_MASTER_URL)
84 | return zipfile.ZipFile(io.BytesIO(r.read()))
85 |
86 | import tqdm
87 |
88 | pbar = tqdm.tqdm(unit='B', unit_scale=True, desc=url.split('/')[-1])
89 |
90 | def update(b=1, bsize=1, tsize=None):
91 | if tsize is not None:
92 | pbar.total = tsize
93 | pbar.update(b * bsize - pbar.n)
94 |
95 | path, headers = urllib.request.urlretrieve(url, reporthook=update)
96 | pbar.close()
97 |
98 | return zipfile.ZipFile(open(path, 'rb'))
99 |
100 |
101 | def extract_master(zf, to='jupylet', noisy=False):
102 |
103 | for p0 in zf.namelist():
104 | p1 = re.sub(r'jupylet-master', to, p0)
105 | if p1[-1] == '/':
106 | os.makedirs(p1, exist_ok=True)
107 | else:
108 | noisy and print('%s -> %s' % (p0, p1))
109 | open(p1, 'wb').write(zf.read(p0))
110 |
111 |
112 | if sys.argv[-2:] == ['-m', 'download']:
113 |
114 | import urllib.request
115 | import zipfile
116 | import io
117 |
118 | while os.path.exists('jupylet'):
119 | r = input('Target directory ./jupylet/ already exists. Would you like to overwrite it (y/n)? ')
120 | if r == 'n':
121 | sys.exit(0)
122 | if r == 'y':
123 | break
124 |
125 | GITHUB_MASTER_URL = 'https://github.com/nir/jupylet/archive/master.zip'
126 |
127 | sys.stderr.write('Downloading jupylet source code ZIP from %s...\n' % GITHUB_MASTER_URL)
128 | zf = download_url(GITHUB_MASTER_URL, progress=True)
129 |
130 | sys.stderr.write('Extracting source code from ZIP file to ./jupylet/.\n')
131 | extract_master(zf)
132 |
133 | sys.stderr.write('Type "cd ./jupyter/examples" to enter the examples directory.\n')
134 |
135 | sys.exit(0)
136 |
137 |
--------------------------------------------------------------------------------
/jupylet/assets/fonts/SIL Open Font License.txt:
--------------------------------------------------------------------------------
1 | Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
5 |
6 | -----------------------------------------------------------
7 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
8 | -----------------------------------------------------------
9 |
10 | PREAMBLE
11 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
12 |
13 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
14 |
15 | DEFINITIONS
16 | "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
17 |
18 | "Reserved Font Name" refers to any names specified as such after the copyright statement(s).
19 |
20 | "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
21 |
22 | "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
23 |
24 | "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
25 |
26 | PERMISSION & CONDITIONS
27 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
28 |
29 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
30 |
31 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
32 |
33 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
34 |
35 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
36 |
37 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
38 |
39 | TERMINATION
40 | This license becomes null and void if any of the above conditions are not met.
41 |
42 | DISCLAIMER
43 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
--------------------------------------------------------------------------------
/jupylet/assets/fonts/SourceSerifPro-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/jupylet/assets/fonts/SourceSerifPro-Bold.otf
--------------------------------------------------------------------------------
/jupylet/assets/shaders/default-vertex-shader.glsl:
--------------------------------------------------------------------------------
1 | #version 330 core
2 |
3 | in vec3 in_position;
4 | in vec3 in_normal;
5 | in vec2 in_texcoord_0;
6 |
7 | uniform mat4 model;
8 | uniform mat4 view;
9 | uniform mat4 projection;
10 |
11 | out vec4 frag_view;
12 | out vec3 vert_position;
13 | out vec3 frag_position;
14 | out vec3 frag_normal;
15 | out vec2 frag_uv;
16 |
17 | struct Skybox {
18 |
19 | int render_skybox;
20 | int texture_exists;
21 | float intensity;
22 | samplerCube texture;
23 | };
24 |
25 | uniform Skybox skybox;
26 |
27 | struct Camera {
28 |
29 | vec3 position;
30 | float zfar;
31 | };
32 |
33 | uniform Camera camera;
34 |
35 | #define DIRECTIONAL_LIGHT 0
36 | #define POINT_LIGHT 1
37 | #define SPOT_LIGHT 2
38 |
39 | #define MAX_CASCADES 4
40 |
41 | struct ShadowmapTexture {
42 | int layer;
43 | float depth;
44 | mat4 projection;
45 | };
46 |
47 | struct Light {
48 |
49 | int type;
50 |
51 | vec3 position;
52 | vec3 direction;
53 |
54 | vec3 color;
55 | float intensity;
56 | float ambient;
57 |
58 | float inner_cone;
59 | float outer_cone;
60 |
61 | float scale;
62 | float snear;
63 |
64 | int shadows;
65 |
66 | ShadowmapTexture shadowmap_textures[MAX_CASCADES];
67 |
68 | int shadowmap_textures_count;
69 |
70 | int shadowmap_pcf;
71 | float shadowmap_bias;
72 | mat4 shadowmap_projection;
73 | };
74 |
75 | #define MAX_LIGHTS 12
76 |
77 | uniform Light lights[MAX_LIGHTS];
78 | uniform int nlights;
79 |
80 | uniform int shadowmap_pass;
81 | uniform int shadowmap_light;
82 |
83 |
84 | void main()
85 | {
86 | vec4 mp4 = model * vec4(in_position, 1.0);
87 |
88 | if (shadowmap_pass == 1) {
89 |
90 | int li = shadowmap_light;
91 |
92 | gl_Position = lights[li].shadowmap_projection * mp4;
93 |
94 | vec3 light_direction;
95 |
96 | if (lights[li].type == DIRECTIONAL_LIGHT) {
97 | light_direction = normalize(lights[li].direction);
98 | }
99 | else {
100 | frag_position = vec3(mp4);
101 | light_direction = normalize(lights[li].position - frag_position);
102 | }
103 |
104 | frag_normal = normalize(mat3(transpose(inverse(model))) * in_normal);
105 |
106 | float nl = dot(frag_normal, light_direction);
107 | if (nl < 0.001) {
108 | return;
109 | }
110 |
111 | float bias = lights[li].shadowmap_bias / nl;
112 |
113 | if (lights[li].type == DIRECTIONAL_LIGHT) {
114 | gl_Position.z += bias * lights[li].scale / 100;
115 | return;
116 | }
117 |
118 | float d0 = length(frag_position - lights[li].position);
119 |
120 | bias *= 2 * lights[li].snear / d0;
121 |
122 | gl_Position.z += bias * gl_Position.w;
123 |
124 | return;
125 | }
126 |
127 | frag_view = view * mp4;
128 |
129 | if (skybox.texture_exists == 1 && skybox.render_skybox == 1) {
130 |
131 | mat4 model = mat4(1.0);
132 | model[3].xyz = camera.position;
133 |
134 | gl_Position = projection * view * model * vec4(in_position, 1.0);
135 | gl_Position = gl_Position.xyww;
136 |
137 | vert_position = in_position;
138 | }
139 | else {
140 | gl_Position = projection * frag_view;
141 | }
142 |
143 | frag_position = vec3(mp4);
144 | frag_normal = mat3(transpose(inverse(model))) * in_normal;
145 | frag_uv = vec2(in_texcoord_0.x, 1.0 - in_texcoord_0.y);
146 | }
147 |
148 |
--------------------------------------------------------------------------------
/jupylet/assets/shaders/shadertoy-wrapper.glsl:
--------------------------------------------------------------------------------
1 |
2 | #version 330
3 |
4 | #if defined VERTEX_SHADER
5 |
6 | in vec3 in_position;
7 | in vec2 in_texcoord_0;
8 |
9 | uniform mat4 jpl_model;
10 | uniform mat4 jpl_projection;
11 |
12 | out vec2 jpl_frag_uv;
13 |
14 | void main() {
15 | gl_Position = jpl_projection * jpl_model * vec4(in_position, 1.0);
16 | jpl_frag_uv = in_texcoord_0;
17 | }
18 |
19 | #elif defined FRAGMENT_SHADER
20 |
21 | uniform vec3 iResolution; // viewport resolution (in pixels)
22 | uniform float iTime; // shader playback time (in seconds)
23 | uniform float iTimeDelta; // render time (in seconds)
24 | uniform int iFrame; // shader playback frame
25 | uniform float iChannelTime[4]; // channel playback time (in seconds)
26 | uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
27 | uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click
28 | uniform sampler2D iChannel0; // input channel. XX = 2D/Cube
29 | uniform sampler2D iChannel1; // input channel. XX = 2D/Cube
30 | uniform sampler2D iChannel2; // input channel. XX = 2D/Cube
31 | uniform sampler2D iChannel3; // input channel. XX = 2D/Cube
32 | uniform vec4 iDate; // (year, month, day, time in seconds)
33 | uniform float iSampleRate; // sound sample rate (i.e., 44100)
34 |
35 |
36 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) {}
37 |
38 |
39 | uniform int jpl_components;
40 | uniform vec4 jpl_color;
41 |
42 | in vec2 jpl_frag_uv;
43 |
44 | out vec4 fragColor;
45 |
46 |
47 | void main() {
48 |
49 | vec2 uv = jpl_frag_uv;
50 |
51 | uv *= iResolution.xy;
52 |
53 | fragColor = jpl_color;
54 |
55 | vec4 color0;
56 |
57 | mainImage(color0, uv);
58 |
59 | if (jpl_components == 4) {
60 | fragColor *= color0;
61 | }
62 | else if (jpl_components == 1) {
63 | fragColor.a *= color0.x;
64 | }
65 | else {
66 | fragColor.rgb *= color0.rgb;
67 | }
68 | }
69 |
70 | #endif
71 |
--------------------------------------------------------------------------------
/jupylet/assets/shaders/sprite.glsl:
--------------------------------------------------------------------------------
1 |
2 | #version 330
3 |
4 | #if defined VERTEX_SHADER
5 |
6 | in vec3 in_position;
7 | in vec2 in_texcoord_0;
8 |
9 | uniform mat4 model;
10 | uniform mat4 projection;
11 |
12 | out vec2 frag_uv;
13 |
14 | void main() {
15 | gl_Position = projection * model * vec4(in_position, 1.0);
16 | frag_uv = in_texcoord_0;
17 | }
18 |
19 | #elif defined FRAGMENT_SHADER
20 |
21 | out vec4 fragColor;
22 |
23 | in vec2 frag_uv;
24 |
25 | uniform sampler2D texture0;
26 |
27 | uniform vec4 color;
28 |
29 | uniform int components;
30 | uniform int flip;
31 |
32 | void main() {
33 |
34 | vec2 uv = frag_uv;
35 |
36 | if (flip == 1) {
37 | uv.y = 1.0 - frag_uv.y;
38 | }
39 |
40 | fragColor = color;
41 |
42 | if (components == 4) {
43 | fragColor *= texture(texture0, uv);
44 | }
45 | else if (components == 1) {
46 | fragColor.a *= texture(texture0, uv).x;
47 | }
48 | else {
49 | fragColor.rgb *= texture(texture0, uv).rgb;
50 | }
51 | }
52 |
53 | #endif
54 |
--------------------------------------------------------------------------------
/jupylet/assets/sounds/impulses/InsidePiano.flac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/jupylet/assets/sounds/impulses/InsidePiano.flac
--------------------------------------------------------------------------------
/jupylet/assets/sounds/impulses/MaesHowe.flac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/jupylet/assets/sounds/impulses/MaesHowe.flac
--------------------------------------------------------------------------------
/jupylet/assets/sounds/impulses/README.md:
--------------------------------------------------------------------------------
1 | # Impulse Response Files
2 |
3 | An impulse response is an acoustic signature used by the process of convolution
4 | reverb to simulate the acoustic reverberation characteristic of a particular
5 | physical location like a concert chamber, etc...
6 |
7 | ### The impulse response files included here and their licenses
8 |
9 | ## Maes Howe
10 |
11 | This impulse response adds a sense of space and a touch of realism to the
12 | output audio stream. It does not produce a lot of echo and is not heavy
13 | computationally.
14 |
15 | **Description by author:** Maes-Howe, Orkney, is one of the finest chambered cairns in Europe, and is dated to 3000BC. Prior work in the acoustics of ancient sites explores how the resonances exhibited therein might have affected regular human ritual and interaction with the space. [Read More](https://www.openair.hosted.york.ac.uk/?page_id=602)
16 |
17 | **Author:** Damian T. Murphy, Audiolab, University of York - [www.openairlib.net](https://www.openairlib.net/)
18 |
19 | **License:** Attribution 4.0 International (CC BY 4.0)
20 |
21 | ## St Andrew’s Church
22 |
23 | As its name suggests, this impulse response gives the feeling of playing an
24 | instrument in a big hall with a beautiful moderate echo.
25 |
26 | **Description by author:** St Andrew’s Church, built in the 14th Century, has one of the finest examples of in-situ acoustic jars (vases or pots) in the UK. These jars were common to European church construction in the late Middle Ages and are said to be based on the ideas of Roman architect Vitruvius, who discussed the use of resonant jars in the design of amphitheatres to provide clarity of voice presentation. [Read More](https://www.openair.hosted.york.ac.uk/?page_id=683)
27 |
28 | **Author:** Damian T. Murphy, Audiolab, University of York - [www.openairlib.net](https://www.openairlib.net/)
29 |
30 | **License:** Attribution 4.0 International (CC BY 4.0)
31 |
32 | ## Inside Piano
33 |
34 | A beatutiful echo. This impulse response is computationally heavier than the
35 | other two. If it makes your computer sweat try running it with a little bit
36 | of extra buffering.
37 |
38 | **Description by author:** An impulse response recorded from just outside a Yamaha baby grand piano with the damper pedal down.
39 |
40 | **Author:** William Andrew Burnson (2013) - [reverbjs.org](http://reverbjs.org/)
41 |
42 | **License:** Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
--------------------------------------------------------------------------------
/jupylet/assets/sounds/impulses/StAndrewsChurch.flac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nir/jupylet/7e1fca6365380567106140fbade48687a378dc36/jupylet/assets/sounds/impulses/StAndrewsChurch.flac
--------------------------------------------------------------------------------
/jupylet/audio/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/audio/__init__.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import asyncio
29 | import pathlib
30 | import time
31 | import os
32 |
33 | from ..utils import callerframe, callerpath
34 |
35 |
36 | def sonic_py(resource_dir='.'):
37 | """Start an audio application.
38 |
39 | An audio application is need to run live loops.
40 |
41 | Args:
42 | resource_dir (str): Path to root of resource dir, for samples, etc...
43 |
44 | Returns:
45 | App: A running application object.
46 | """
47 | from ..app import App
48 |
49 | red = os.path.join(callerpath(), resource_dir)
50 | red = pathlib.Path(red).absolute()
51 |
52 | app = App(32, 32, resource_dir=str(red))
53 | app.run(0)
54 |
55 | return app
56 |
57 |
58 | DEFAULT_AMP = 0.5
59 |
60 | MIDDLE_C = 261.63
61 |
62 | FPS = 44100
63 |
64 |
65 | def t2frames(t):
66 | """Convert time in seconds to frames at 44100 frames per second.
67 |
68 | Args:
69 | t (float): The time duration in seconds.
70 |
71 | Returns:
72 | int: The number of frames.
73 | """
74 | return int(FPS * t)
75 |
76 |
77 | def frames2t(frames):
78 | """Convert frames at 44100 frames per second to time in seconds.
79 |
80 | Args:
81 | frames (int): The number of frames.
82 |
83 | Returns:
84 | float: The time duration in seconds.
85 | """
86 | return frames / FPS
87 |
88 |
89 | def get_time():
90 | return time.time()
91 |
92 |
93 | _note_value = 4
94 |
95 |
96 | def set_note_value(v=4):
97 | """Set the note value representing one beat.
98 |
99 | Args:
100 | v (float): Note value.
101 | """
102 | global _note_value
103 | _note_value = v
104 |
105 |
106 | def get_note_value():
107 | return _note_value
108 |
109 |
110 | _bpm = 240
111 |
112 |
113 | def set_bpm(bpm=240):
114 | """Set the tempo to the given beats per minute.
115 |
116 | Args:
117 | bpm (float): Beats per minute.
118 | """
119 | global _bpm
120 | _bpm = bpm
121 |
122 |
123 | def get_bpm():
124 | return _bpm
125 |
126 |
127 | dtd = {}
128 | syd = {}
129 |
130 |
131 | def use(sound, **kwargs):
132 | """Set the instrument to use in subsequent calls to :func:`play`.
133 |
134 | You can supply key/value pairs of properties to modify in the given
135 | instrument. If you do, the instrument will be copied first, and
136 | the modifications will be applied to the new copy.
137 |
138 | Args:
139 | sound (GatedSound): Instrument to use.
140 | **kwargs: Properties of intrument to modify.
141 | """
142 | if kwargs:
143 | sound = sound.copy().set(**kwargs)
144 |
145 | cf = callerframe()
146 | cn = cf.f_code.co_name
147 |
148 | if cn in ['', 'async-def-wrapper']:
149 | hh = ''
150 | elif cn.startswith('', 'async-def-wrapper']:
179 | hh = ''
180 | elif cn.startswith('', 'async-def-wrapper']:
223 | hh = ''
224 | elif cn.startswith(' 0.5 else n1
91 |
92 | return _n + str(int(octave))
93 |
94 |
--------------------------------------------------------------------------------
/jupylet/audio/synth.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/audio/synth.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import logging
29 |
30 | from .sound import Sound, GatedSound, Envelope, Oscillator, Noise, noise_color
31 | from .sound import PhaseModulator
32 | from .effects import SchroederReverb, Overdrive
33 | from .filters import ResonantFilter
34 |
35 | from ..audio import note, DEFAULT_AMP
36 |
37 | import numpy as np
38 |
39 |
40 | logger = logging.getLogger(__name__)
41 |
42 |
43 | class Synth(GatedSound):
44 |
45 | def __init__(self, amp=DEFAULT_AMP, pan=0., duration=None):
46 |
47 | super().__init__(amp=amp, pan=pan, duration=duration)
48 |
49 | self.env0 = Envelope(0.03, 0.3, 0.7, 1., linear=False)
50 | self.osc0 = Oscillator('sine', 4)
51 | self.osc1 = Oscillator('tri')
52 |
53 | def forward(self):
54 |
55 | self.osc1.freq = self.freq
56 |
57 | g0 = self.gate()
58 | e0 = self.env0(g0)
59 |
60 | o0 = self.osc0()
61 | o1 = self.osc1(key_modulation=o0/2)
62 |
63 | return o1 * e0
64 |
65 |
66 | class Drums(GatedSound):
67 |
68 | def __init__(self, amp=DEFAULT_AMP, pan=0.):
69 |
70 | super().__init__(amp=amp, pan=pan)
71 |
72 | self.env0 = Envelope(0.002, 0.15, 0., 0., linear=False)
73 | self.noise = Noise()
74 |
75 | def forward(self):
76 |
77 | color = (self.key - note.C1) / (note.B7 - note.C1) * 12 - 6
78 |
79 | g0 = self.gate()
80 | e0 = self.env0(g0)
81 | a0 = self.noise(color)
82 |
83 | return a0 * e0
84 |
85 |
86 | drawbars = [16, 5+1/3, 8, 4, 2+2/3, 2, 1+3/5, 1+1/3, 1]
87 |
88 | def d2f(d):
89 | return 440 * 16 / (drawbars[d])
90 |
91 |
92 | class Chorus(Sound):
93 |
94 | def __init__(self, mix=0.5, depth=1/3, shared=False):
95 |
96 | super().__init__(shared=shared)
97 |
98 | self.mix = mix
99 | self.depth = depth
100 |
101 | self.osc = Oscillator('tri', freq=7)
102 | self.phm = PhaseModulator(beta=0.85*44.1)
103 |
104 | def forward(self, x):
105 |
106 | if self.mix <= 0:
107 | return x
108 |
109 | vo = self.osc()
110 | vb = self.phm(x, vo * self.depth)
111 |
112 | return x * (1 - self.mix) + vb * self.mix
113 |
114 | @property
115 | def vibrato_and_chorus(self):
116 |
117 | mode = max(0, min(2, round(self.mix * 2)))
118 | if mode == 0:
119 | return None
120 |
121 | mode = 'c' if mode == 1 else 'v'
122 | valu = max(1, min(3, round(self.depth * 3)))
123 |
124 | return mode + str(int(valu))
125 |
126 | @vibrato_and_chorus.setter
127 | def vibrato_and_chorus(self, v):
128 |
129 | assert v in ['c1', 'c2', 'c3', 'v1', 'v2', 'v3', None]
130 |
131 | if v is None:
132 | self.mix = 0
133 | return
134 |
135 | self.mix = 0.5 if v[0] == 'c' else 1.
136 | self.depth = float(v[1]) / 3
137 |
138 |
139 | class Hammond(GatedSound):
140 |
141 | def __init__(self, configuration='888000000', amp=DEFAULT_AMP, pan=0., duration=None):
142 |
143 | super().__init__(amp=amp, pan=pan, duration=duration)
144 |
145 | self.configuration = configuration
146 |
147 | self.reve = SchroederReverb(mix=0.25, rt=0.750, shared=True)
148 | self.over = Overdrive(gain=1/amp, amp=amp, shared=True)
149 | self.chor = Chorus(shared=True)
150 |
151 | self.leak = Noise(noise_color.violet)
152 | self.env0 = Envelope(0.005, 0., 1., 0.01, linear=False)
153 | self.prec = Envelope(0., 0.2, 0., 0.01, linear=False)
154 |
155 | self.bass = Oscillator(freq=440)
156 | self.quin = Oscillator(freq=1320)
157 | self.neut = Oscillator(freq=880)
158 | self.octa = Oscillator(freq=1760)
159 | self.naza = Oscillator(freq=2640)
160 | self.bloc = Oscillator(freq=3520)
161 | self.tier = Oscillator(freq=4400)
162 | self.lari = Oscillator(freq=5280)
163 | self.siff = Oscillator(freq=7040)
164 |
165 | self.chorus = True
166 | self.reverb = True
167 | self.overdrive = True
168 |
169 | self.leak_gain = 0.05
170 |
171 | self.precussion = True
172 | self.precussion_gain = 1.5
173 | self.precussion_decay = 0.2
174 | self.precussion_drawbar = 3
175 |
176 | def get_effects(self):
177 |
178 | el = []
179 |
180 | if self.chorus:
181 | el.append(self.chor)
182 |
183 | if self.reverb:
184 | el.append(self.reve)
185 |
186 | if self.overdrive:
187 | el.append(self.over)
188 |
189 | return tuple(el) + self._effects
190 |
191 | def get_vibrato_and_chorus(self):
192 | return self.chor.vibrato_and_chorus
193 |
194 | def set_vibrato_and_chorus(self, v):
195 | self.chor.vibrato_and_chorus = v
196 |
197 | def parse_configuration(self, c):
198 | return [(i, float(c) / 8) for i, c in enumerate(c)]
199 |
200 | def get_drawbar(self, i):
201 | dbl = ['bass', 'quin', 'neut', 'octa', 'naza', 'bloc', 'tier', 'lari', 'siff']
202 | return getattr(self, dbl[i])
203 |
204 | def forward(self):
205 |
206 | self.prec.decay = self.precussion_decay
207 |
208 | g0 = self.gate()
209 | e0 = self.env0(g0)
210 | ep = self.prec(g0)
211 |
212 | km = self.key - 69
213 |
214 | al = []
215 |
216 | for i, a in self.parse_configuration(self.configuration):
217 |
218 | if i == self.precussion_drawbar and self.precussion:
219 |
220 | db = self.get_drawbar(i)
221 | a0 = db(key_modulation=km)
222 | al.append(a0 * self.precussion_gain * ep)
223 |
224 | elif a > 0:
225 |
226 | db = self.get_drawbar(i)
227 | a0 = db(key_modulation=km)
228 | al.append(a0 * a)
229 |
230 | a0 = np.stack(al).sum(0)
231 | a1 = a0 + self.leak() * self.leak_gain * ep
232 | a2 = a1 * e0
233 |
234 | return a2
235 |
236 |
237 | class TB303(GatedSound):
238 |
239 | def __init__(
240 | self,
241 | shape='sawtooth',
242 | resonance=1,
243 | cutoff=0,
244 | decay=2,
245 | amp=DEFAULT_AMP,
246 | pan=0.,
247 | duration=None
248 | ):
249 |
250 | super().__init__(amp=amp, pan=pan, duration=duration)
251 |
252 | self.shape = shape
253 | self.resonance = resonance
254 | self.cutoff = cutoff
255 | self.decay = decay
256 |
257 | self.env0 = Envelope(0.01, 0., 1., 0.01)
258 | self.env1 = Envelope(0., decay, 0., 0., linear=False)
259 |
260 | self.osc0 = Oscillator(shape)
261 |
262 | self.filter = ResonantFilter(btype='lowpass', resonance=resonance)
263 |
264 | def forward(self):
265 |
266 | g0 = self.gate()
267 |
268 | e0 = self.env0(g0)
269 | e1 = self.env1(g0, decay=self.decay) * 12 * 8
270 |
271 | a0 = self.osc0(shape=self.shape, freq=self.freq)
272 |
273 | a1 = self.filter(
274 | a0,
275 | key_modulation=e1+self.cutoff,
276 | resonance=self.resonance,
277 | freq=self.freq,
278 | )
279 |
280 | return a1 * e0
281 |
282 |
283 | tb303 = TB303()
284 |
285 |
--------------------------------------------------------------------------------
/jupylet/collision.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/collision.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import functools
29 | import math
30 | import glm
31 |
32 | import PIL.Image
33 | import scipy.signal
34 |
35 | import numpy as np
36 |
37 | from .resource import pil_resize_to
38 |
39 |
40 | MAP_SIZE = 128
41 |
42 |
43 | def affine(a=0, s=1, ax=0, ay=0, dx=0, dy=0):
44 |
45 | r = math.radians(a)
46 | a = math.cos(r) * s
47 | b = math.sin(r) * s
48 |
49 | return np.array([
50 | [a, b, -a*ax - b*ay + dx],
51 | [-b, a, b*ax - a*ay + dy],
52 | [0, 0, 1]
53 | ])
54 |
55 |
56 | """
57 | @functools.lru_cache(1024)
58 | def trbl0(width, height, anchor_x=0, anchor_y=0, angle=0, scale=1):
59 |
60 | bb0 = np.array([[width, height, 1], [width, 0, 1], [0, 0, 1], [0, height, 1]])
61 | bb1 = affine0(angle, scale, anchor_x, anchor_y).dot(bb0.T).T
62 | bb2 = bb1.tolist()
63 |
64 | x = [v[0] for v in bb2]
65 | y = [v[1] for v in bb2]
66 |
67 | t, r, b, l = max(y), max(x), min(y), min(x)
68 |
69 | return t, r, b, l
70 | """
71 |
72 | def affine0(a=0, s=1, ax=0, ay=0, dx=0, dy=0):
73 |
74 | r = math.radians(a)
75 | a = math.cos(r) * s
76 | b = math.sin(r) * s
77 |
78 | return glm.mat3(
79 | a, b, -a*ax - b*ay + dx,
80 | -b, a, b*ax - a*ay + dy,
81 | 0, 0, 1
82 | )
83 |
84 |
85 | @functools.lru_cache(1024)
86 | def trbl(width, height, anchor_x=0, anchor_y=0, angle=0, scale=1):
87 |
88 | bb0 = glm.mat4x3(
89 | width, height, 1,
90 | width, 0, 1,
91 | 0, 0, 1,
92 | 0, height, 1
93 | )
94 |
95 | bb1 = glm.transpose(bb0) * affine0(angle, scale, anchor_x, anchor_y)
96 |
97 | x = bb1[0]
98 | y = bb1[1]
99 |
100 | t, r, b, l = max(y), max(x), min(y), min(x)
101 |
102 | return t, r, b, l
103 |
104 |
105 | def hitmap_and_outline_from_alpha(im):
106 |
107 | if isinstance(im, np.ndarray):
108 | assert max(im.shape[:2]) == MAP_SIZE, 'Maximum size of array expected to be %s.' % MAP_SIZE
109 | else:
110 | im = pil_resize_to(im, MAP_SIZE)
111 |
112 | a0 = np.array(im)
113 |
114 | if len(a0.shape) == 3:
115 | a0 = a0[...,-1]
116 |
117 | k0 = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]])
118 |
119 | a2 = (a0 > 128).astype('uint8')[::-1]
120 | a3 = scipy.signal.convolve2d(a2, k0, 'same') != 4
121 | a4 = a2 * a3
122 | a5 = np.stack(a4.nonzero()[::-1], -1)
123 | a6 = np.concatenate([a5, a5[:,:1] * 0 + 1], -1)
124 |
125 | h1, w1 = a2.shape
126 | xx = max(a2.shape) + 2
127 |
128 | a7 = np.pad(a2, ((1, xx - h1 - 1), (1, xx - w1 - 1)))
129 |
130 | return a7, a6
131 |
132 |
133 | def collisions_from_hitmap_and_outline(a0, a1):
134 | a1 = np.core.umath.clip(a1, 0, a0.shape[0]-1)
135 | a2 = a0[a1[:,1], a1[:,0]]
136 | a3 = a1[a2.nonzero()]
137 | return a3
138 |
139 |
140 | def compute_collisions(o0, o1, debug=False):
141 |
142 | s0 = max(o0.height, o0.width) / MAP_SIZE
143 | s1 = max(o1.height, o1.width) / MAP_SIZE
144 |
145 | dr = o1.angle - o0.angle
146 | r0 = o1.angle * math.pi / 180 * -1
147 |
148 | dy0 = o0.y - o1.y
149 | dx0 = o0.x - o1.x
150 |
151 | dx1 = math.cos(r0) * dx0 - math.sin(r0) * dy0
152 | dy1 = math.cos(r0) * dy0 + math.sin(r0) * dx0
153 |
154 | dy2 = (dy1 + 1 + o1.anchor.y * o1.height) / s1
155 | dx2 = (dx1 + 1 + o1.anchor.x * o1.width) / s1
156 |
157 | af = affine(
158 | dr,
159 | s0 / s1,
160 | o0.anchor.x * o0.width / s0,
161 | o0.anchor.y * o0.height / s0, dx2,
162 | dy2
163 | )
164 |
165 | oo = af.dot(o0.outline.T)[:2].T.astype('int64')
166 | cl = collisions_from_hitmap_and_outline(o1.hitmap, oo)
167 |
168 | if not debug:
169 | return cl - (o1.anchor.x * o1.width / s1, o1.anchor.y * o1.height / s1)
170 |
171 | #
172 | # Use the following code to display debug output in a jupyter notebook:
173 | #
174 | # fig = plt.figure(figsize=(8, 8))
175 | # ax = fig.add_subplot(111)
176 | # ax.set_aspect(1.)
177 | # _ = ax.plot(hm[:,1], hm[:,0], 'y.', oo[:,0], oo[:,1], 'r.', cl[:,0], cl[:,1], 'bo')
178 | #
179 |
180 | hm = np.argwhere(o1.hitmap > 0)
181 | return cl, oo, hm
182 |
183 |
--------------------------------------------------------------------------------
/jupylet/color.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/color.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import functools
29 | import webcolors
30 | import glm
31 |
32 |
33 | @functools.lru_cache(maxsize=1024)
34 | def parse_webcolor(color):
35 |
36 | if color[0] == '#':
37 | return glm.vec3(webcolors.hex_to_rgb(color)) / 255
38 |
39 | return glm.vec3(webcolors.name_to_rgb(color)) / 255
40 |
41 |
42 | def c2v(color, alpha=1.):
43 |
44 | if type(color) is str:
45 | color = parse_webcolor(color)
46 |
47 | if len(color) == 3:
48 | color = glm.vec4(color, alpha)
49 |
50 | return glm.vec4(color)
51 |
52 |
--------------------------------------------------------------------------------
/jupylet/env.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/env.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import functools
29 | import argparse
30 | import platform
31 | import inspect
32 | import sys
33 | import os
34 |
35 | import multiprocessing as mp
36 |
37 |
38 | def parse_args():
39 | return create_parser().parse_args(sys.argv[1:])
40 |
41 |
42 | def create_parser():
43 |
44 | parser = argparse.ArgumentParser()
45 |
46 | parser.add_argument(
47 | "--window",
48 | choices=['pyglet', 'glfw'],
49 | help="Windowing library to use.",
50 | )
51 |
52 | parser.add_argument(
53 | "--log_level",
54 | choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
55 | help="logging level.",
56 | )
57 |
58 | return parser
59 |
60 |
61 | _window_size = None
62 |
63 |
64 | def set_window_size(size):
65 | global _window_size
66 | _window_size = size
67 |
68 |
69 | def get_window_size():
70 | return _window_size
71 |
72 |
73 | _is_rl_worker = False
74 |
75 |
76 | def set_rl_worker():
77 | global _is_rl_worker
78 | _is_rl_worker = True
79 |
80 |
81 | def is_rl_worker():
82 | return _is_rl_worker
83 |
84 |
85 | @functools.lru_cache()
86 | def is_remote():
87 |
88 | if is_binder_env():
89 | return True
90 |
91 | if is_aws_linux():
92 | return True
93 |
94 |
95 | def is_aws_linux():
96 |
97 | if platform.system() == 'Linux':
98 | cmd = 'find /sys/devices/virtual/dmi/id/ -type f | xargs grep "Amazon EC2" 2> /dev/null'
99 | return 'Amazon' in os.popen(cmd).read()
100 |
101 |
102 | def is_binder_env():
103 | return 'BINDER_REQUEST' in os.environ
104 |
105 |
106 | def is_numpy_openblas():
107 | import numpy
108 |
109 | if hasattr(numpy.__config__, 'get_info'):
110 | ll = numpy.__config__.get_info('blas_opt_info').get('libraries', [])
111 | for l in ll:
112 | if 'openblas' in l:
113 | return True
114 | return False
115 |
116 | return 'openblas' in repr(numpy.__config__.show('dicts'))
117 |
118 |
119 | def is_osx():
120 | return platform.system().lower() == 'darwin'
121 |
122 |
123 | _has_display = None
124 |
125 |
126 | def has_display():
127 |
128 | global _has_display
129 |
130 | if _has_display is not None:
131 | return _has_display
132 |
133 | v = mp.Value('i', 0)
134 |
135 | if 'pyglet' in sys.modules:
136 | _has_display0(v)
137 |
138 | else:
139 | p = mp.Process(target=_has_display0, args=(v,))
140 | p.start()
141 | p.join()
142 |
143 | _has_display = v.value
144 |
145 | return _has_display
146 |
147 |
148 | def _has_display0(v):
149 |
150 | try:
151 | import pyglet
152 | pyglet.canvas.get_display()
153 | v.value = 1
154 | except:
155 | pass
156 |
157 |
158 | _xvfb = None
159 |
160 |
161 | def start_xvfb():
162 |
163 | global _xvfb
164 |
165 | if platform.system() == 'Linux' and _xvfb is None:
166 |
167 | import xvfbwrapper
168 | _xvfb = xvfbwrapper.Xvfb()
169 | _xvfb.start()
170 |
171 |
172 | def is_xvfb():
173 | return _xvfb is not None
174 |
175 |
176 | @functools.lru_cache()
177 | def is_python_script():
178 |
179 | f0 = inspect.currentframe()
180 |
181 | while f0:
182 | if not f0.f_back and f0.f_globals.get('__name__') == '__main__':
183 | return True
184 |
185 | f0 = f0.f_back
186 |
187 | return False
188 |
189 |
190 | @functools.lru_cache()
191 | def is_jupyter():
192 | try:
193 | from IPython import get_ipython
194 | if 'IPKernelApp' not in get_ipython().config: # Checks for non-notebook IPython kernels
195 | return False
196 | except (ImportError, AttributeError):
197 | return False
198 | return True
199 |
200 |
201 | def is_sphinx_build():
202 | return 'SPHINXBUILD' in os.environ
203 |
204 |
--------------------------------------------------------------------------------
/jupylet/label.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/label.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import functools
29 | import math
30 | import io
31 | import os
32 |
33 | import PIL.Image
34 | import PIL.ImageDraw
35 | import PIL.ImageFont
36 |
37 | import moderngl_window as mglw
38 | import numpy as np
39 |
40 | from moderngl_window.meta import DataDescription
41 |
42 | from .sprite import Sprite
43 | from .state import State
44 |
45 |
46 | def rtl(s):
47 | return str(s[::-1])
48 |
49 |
50 | #
51 | # Note: Find free fonts at www.fontsquirrel.com
52 | #
53 |
54 | @functools.lru_cache(maxsize=32)
55 | def load_font(path, size):
56 |
57 | ff = mglw.resources.data.load(DataDescription(path=path, kind='binary'))
58 | return PIL.ImageFont.truetype(io.BytesIO(ff), size)
59 |
60 |
61 | @functools.lru_cache(maxsize=2048)
62 | def draw_chr(c, path, size):
63 |
64 | font = load_font(path, size)
65 |
66 | if hasattr(font, 'getsize'):
67 | w, h = font.getsize(c)
68 | else:
69 | w, h = font.getbbox(c)[2:]
70 |
71 | im = PIL.Image.new('L', (w, h))
72 | di = PIL.ImageDraw.Draw(im)
73 | di.text((0, 0), c, fill='white', font=font)
74 |
75 | return np.array(im)
76 |
77 |
78 | def draw_str(s, path, size, line_height=1.2, align='left'):
79 |
80 | al = []
81 | ll = []
82 |
83 | # Compute line height and baseline height.
84 | lh = math.ceil(size * line_height)
85 | bl = draw_chr('a', path, size).shape[0]
86 |
87 | # Coordinates for top-left position for each char.
88 | hh = 0
89 | ww = 0
90 |
91 | # Maximum accumulated width and height for label.
92 | mh = 0
93 | mw = 0
94 |
95 | for c in s.rstrip():
96 | if c == '\n':
97 | ll.append(ww)
98 | hh += lh
99 | mw = max(mw, ww)
100 | ww = 0
101 | mh = 0
102 | continue
103 |
104 | ca = draw_chr(c, path, size)
105 | al.append((ca, (hh, ww), len(ll)))
106 |
107 | h, w = ca.shape
108 |
109 | mh = max(mh, h)
110 | ww += w
111 |
112 | ll.append(ww)
113 |
114 | # Compute final baseline, maximum width and height for label.
115 | bl = mh - bl
116 | mh = hh + mh
117 | mw = max(mw, ww)
118 |
119 | a0 = np.zeros((mh, mw), dtype='uint8')
120 |
121 | aw = {'left': 0, 'center': 0.5, 'right': 1}[align]
122 |
123 | for ca, (hh, ww), li in al:
124 |
125 | a = int((mw - ll[li]) * aw)
126 | h, w = ca.shape
127 | a0[hh:hh+h, ww+a:ww+a+w] = ca
128 |
129 | return a0, bl
130 |
131 |
132 | class Label(Sprite):
133 |
134 | """A text label.
135 |
136 | Since a text label is actually implemented as a 2D sprite, it has all the
137 | functionality and methods of a Sprite.
138 |
139 | Args:
140 | text (str): text to render as label.
141 | font (path): path to a true type or open type font.
142 | font_size (float): font size to use.
143 | line_height (float): determines the distance between lines.
144 | align (str): the desired alignment for the text label. May be one
145 | of 'left', 'center', and 'right'.
146 | color (str or 3-tuple): a color name, color hex notation, or a
147 | 3-tuple. specifying the color for the text label.
148 | x (float): the x position for the label.
149 | y (float): the y position for the label.
150 | angle (float): clockwise rotation of the label in degrees.
151 | anchor_x (float or str): either 'left', 'center' or 'right' or a
152 | value between 0.0 (for left) and 1.0 (for right) indicating
153 | the anchor point inside the label along its x axis.
154 | anchor_y (float or str): either 'bottom', 'baseline', 'center' or
155 | 'top' or a value between 0.0 (for bottom) and 1.0 (for top)
156 | indicating the anchor point inside the label along its y axis.
157 | """
158 |
159 | def __init__(
160 | self,
161 | text='',
162 | font_path='fonts/SourceSerifPro-Bold.otf',
163 | font_size=16,
164 | line_height=1.2,
165 | align='left',
166 | bold=False,
167 | italic=False,
168 | color='white',
169 | x=0,
170 | y=0,
171 | angle=0.0,
172 | width=None,
173 | height=None,
174 | anchor_x='left',
175 | anchor_y='baseline',
176 | ):
177 | """"""
178 |
179 | image, baseline = draw_str(text, font_path, font_size, line_height, align)
180 |
181 | super().__init__(
182 | image,
183 | x,
184 | y,
185 | angle=angle,
186 | anchor_x=anchor_x,
187 | anchor_y=anchor_y,
188 | height=height,
189 | width=width,
190 | collisions=False,
191 | )
192 |
193 | self._items = dict(
194 | text = text,
195 | font_path = font_path,
196 | font_size = font_size,
197 | line_height = line_height,
198 | align = align,
199 | )
200 |
201 | self.baseline = baseline / self.texture.height
202 |
203 | self.color = color
204 |
205 | def update(self, shader):
206 |
207 | if self._dirty:
208 | self._dirty.clear()
209 |
210 | self.image, baseline = draw_str(
211 | self.text,
212 | self.font_path,
213 | self.font_size,
214 | self.line_height,
215 | self.align,
216 | )
217 |
218 | self.baseline = baseline / self.texture.height
219 |
220 | if self._ay == 'baseline':
221 | self.anchor.y = self.baseline
222 |
223 | def get_state(self):
224 | return dict(
225 | sprite = super().get_state(),
226 | text = self.text,
227 | font_path = self.font_path,
228 | font_size = self.font_size,
229 | line_height = self.line_height,
230 | align = self.align,
231 | )
232 |
233 | def set_state(self, s):
234 |
235 | super().set_state(s['sprite'])
236 |
237 | for k, v in s.items():
238 | if k != 'sprite':
239 | setattr(self, k, v)
240 |
241 |
--------------------------------------------------------------------------------
/jupylet/lru.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/lru.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import logging
29 |
30 |
31 | logger = logging.getLogger(__name__)
32 |
33 |
34 | SPRITE_TEXTURE_UNIT = 0
35 | SKYBOX_TEXTURE_UNIT = 1
36 | SHADOW_TEXTURE_UNIT = 2
37 | TARRAY_TEXTURE_UNIT = 3
38 |
39 |
40 | # TODO: an lru is actually not an ideal policy for allocating texture units.
41 | # fix it.
42 |
43 | class LRU(object):
44 | """Mechanism to allocate least recently used slot in array."""
45 |
46 | def __init__(self, min_items, max_items):
47 |
48 | self.mini = min_items
49 | self.step = max_items
50 | self.items = {i: [i, i, i, 0] for i in range(min_items, max_items)}
51 |
52 | def reset(self, min_items, max_items):
53 |
54 | self.mini = min_items
55 | self.step = max_items
56 | self.items = {i: [i, i, i, 0] for i in range(min_items, max_items)}
57 |
58 | def allocate(self, lid=None):
59 | """Allocate slot.
60 |
61 | Args:
62 | lid (int): An id that identifies "object" in array. A new id will
63 | be generated if None is given.
64 |
65 | Returns:
66 | tuple: A 4-tuple consisting of (step, lid, slot, new) where
67 | *step* indicates the lru "timestamp" for this object,
68 | *lid* is the allocated object id,
69 | *slot* is the array index allocated for the "object", and
70 | *new* is 1 if "object" was allocated a new slot or 0 if it
71 | remains in the same slot it was before.
72 | """
73 | self.step += 1
74 |
75 | if lid is None:
76 | lid = self.step
77 |
78 | r = self.items.get(lid)
79 |
80 | if r is None:
81 |
82 | lid0, slot = min(self.items.values())[1:3]
83 | self.items.pop(lid0)
84 | self.items[lid] = [self.step, lid, slot, 0]
85 |
86 | logger.debug('Allocated a new LRU slot with step=%r, lid=%r, slot=%r, 1.', self.step, lid, slot)
87 |
88 | return self.step, lid, slot, 1
89 |
90 | r[0] = self.step
91 |
92 | return r
93 |
94 |
95 | _MAX_MATERIALS = 12
96 | _lru_materials = LRU(0, _MAX_MATERIALS)
97 |
98 |
--------------------------------------------------------------------------------
/jupylet/node.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/node.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import copy
29 | import math
30 | import glm
31 |
32 | from .utils import glm_dumps, glm_loads
33 |
34 |
35 | class Object(object):
36 |
37 | """Implement an object that tracks changes to its properties.
38 |
39 | It is used for implementing lazy mechanisms that only engage in costly
40 | computations when necessary.
41 | """
42 |
43 | def __init__(self, dirty=True):
44 |
45 | self._items = {}
46 | self._dirty = set([True]) if dirty else set()
47 |
48 | def __dir__(self):
49 | return list(self._items.keys()) + super().__dir__()
50 |
51 | def __getattr__(self, k):
52 |
53 | if k not in self._items:
54 | return super().__getattribute__(k)
55 |
56 | return self._items[k]
57 |
58 | def __setattr__(self, k, v):
59 |
60 | if k == '_items' or k not in self._items:
61 | return super().__setattr__(k, v)
62 |
63 | self._items[k] = v
64 | self._dirty.add(k)
65 |
66 | def __repr__(self):
67 | return '%s(%s)' % (type(self).__name__, ', '.join(
68 | '%s=%s' % i for i in list(self.__dict__.items()) + list(self._items.items()) if i[0][0] != '_'
69 | ))
70 |
71 |
72 | def q2aa(rotation, deg=False):
73 | """Transform quaternion to angle+axis."""
74 |
75 | if not rotation or rotation == (1., 0., 0., 0.):
76 | return 0, glm.vec3(0, 0, 1)
77 |
78 | c, xs, ys, zs = rotation #glm.conjugate(rotation)
79 |
80 | angle = math.acos(c) * 2
81 | s = math.sin(angle / 2)
82 |
83 | if s == 0:
84 | return 0, glm.vec3(0, 0, 1)
85 |
86 | if deg:
87 | angle = round(180 * angle / math.pi, 3)
88 |
89 | return angle, glm.vec3(xs / s, ys / s, zs / s)
90 |
91 |
92 | def aa2q(angle, axis=glm.vec3(0, 0, 1)):
93 | return glm.angleAxis(angle, glm.normalize(axis))
94 |
95 |
96 | _i4 = glm.mat4(1.)
97 |
98 |
99 | class Node(Object):
100 |
101 | """Handle and represent geometric operations as matrix transformation.
102 |
103 | Handle and represent scaling, rotation, and translation in local and global
104 | 3D coordinates as a single matrix (lazily maintained) transformation.
105 |
106 | Args:
107 | name (str, optional): Name of object.
108 | anchor (glm.vec3, optional):
109 | """
110 |
111 | def __init__(
112 | self,
113 | name='',
114 | anchor=None,
115 | scale=None,
116 | rotation=None,
117 | position=None,
118 | ):
119 |
120 | super().__init__()
121 |
122 | self.name = name
123 |
124 | self.anchor = glm.vec3(anchor) if anchor else glm.vec3(0.)
125 | self.scale0 = glm.vec3(scale) if scale else glm.vec3(1.)
126 | self.rotation = glm.quat(rotation) if rotation else glm.quat(1., 0., 0., 0.)
127 | self.position = glm.vec3(position) if position else glm.vec3(0.)
128 |
129 | self._itemz = None
130 |
131 | self._matrix = glm.mat4(1.)
132 |
133 | @property
134 | def scale(self):
135 | return self.scale0
136 |
137 | @scale.setter
138 | def scale(self, value):
139 | self.scale0 = value
140 |
141 | @property
142 | def matrix(self):
143 |
144 | if self._itemz != [self.anchor, self.scale0, self.rotation, self.position]:
145 | self._itemz = copy.deepcopy([
146 | self.anchor,
147 | self.scale0,
148 | self.rotation,
149 | self.position
150 | ])
151 |
152 | t0 = glm.translate(_i4, self.position)
153 | r0 = t0 * glm.mat4_cast(self.rotation)
154 | s0 = glm.scale(r0, self.scale0)
155 | a0 = glm.translate(s0, -self.anchor)
156 |
157 | self._matrix = a0
158 |
159 | self._dirty.add('_matrix')
160 |
161 | return self._matrix
162 |
163 | def move_local(self, xyz):
164 | """Move by given displacement in local coordinate system.
165 |
166 | Args:
167 | xyz (glm.vec3): Displacement.
168 | """
169 | rxyz = glm.mat4_cast(self.rotation) * glm.vec4(xyz, 1.)
170 | self.position += rxyz.xyz
171 |
172 | def rotate_local(self, angle, axis=(0., 0., 1.)):
173 | """Rotate counter clockwise by given angle around given axis in local
174 | coordinate system.
175 |
176 | Args:
177 | angle (float): Angle in radians.
178 | axis (glm.vec3): Rotation axis.
179 | """
180 | self.rotation *= aa2q(angle, glm.vec3(axis))
181 |
182 | def move_global(self, xyz):
183 | """Move by given displacement in global coordinate system.
184 |
185 | Args:
186 | xyz (glm.vec3): Displacement.
187 | """
188 | self.position += xyz
189 |
190 | def rotate_global(self, angle, axis=(0., 0., 1.)):
191 | """Rotate counter clockwise by given angle around given axis in global
192 | coordinate system.
193 |
194 | Args:
195 | angle (float): Angle in radians.
196 | axis (glm.vec3): Rotation axis.
197 | """
198 | self.rotation = aa2q(angle, glm.vec3(axis)) * self.rotation
199 |
200 | @property
201 | def up(self):
202 | """glm.vec3: Return the local up (+y) axis."""
203 | return (self.matrix * glm.vec4(0, 1, 0, 0)).xyz
204 |
205 | @property
206 | def front(self):
207 | """glm.vec3: Return the local front (+z) axis."""
208 | return (self.matrix * glm.vec4(0, 0, 1, 0)).xyz
209 |
210 | def get_state(self):
211 | """Get a dictionary of properties defining the object state.
212 |
213 | Returns:
214 | dict: A dictionary of properties.
215 | """
216 | return dict(
217 | rotation = glm_dumps(glm.quat(self.rotation)),
218 | position = glm_dumps(glm.vec3(self.position)),
219 | anchor = glm_dumps(glm.vec3(self.anchor)),
220 | scale0 = glm_dumps(glm.vec3(self.scale0)),
221 | )
222 |
223 | def set_state(self, s):
224 | """Set object state from given dictionary of properties.
225 |
226 | Args:
227 | s (dict): A dictionary of properties previously returned by a call
228 | to ``get_state()``.
229 | """
230 | for k, v in s.items():
231 | setattr(self, k, glm_loads(v))
232 |
233 |
--------------------------------------------------------------------------------
/jupylet/state.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/state.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | class State(object):
29 |
30 | def __init__(self, **kwargs):
31 |
32 | for k, v in kwargs.items():
33 | setattr(self, k, v)
34 |
35 | def __repr__(self):
36 | return repr(self.__dict__)
37 |
38 | def __setitem__(self, key, item):
39 | self.__dict__[key] = item
40 |
41 | def __getitem__(self, key):
42 | return self.__dict__[key]
43 |
44 | def get_state(self):
45 | return self
46 |
47 | def set_state(self, s):
48 | self.__dict__ = vars(s)
49 |
50 |
--------------------------------------------------------------------------------
/jupylet/utils.py:
--------------------------------------------------------------------------------
1 | """
2 | jupylet/utils.py
3 |
4 | Copyright (c) 2022, Nir Aides - nir.8bit@gmail.com
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | """
26 |
27 |
28 | import ipywidgets
29 | import functools
30 | import traceback
31 | import hashlib
32 | import inspect
33 | import logging
34 | import pickle
35 | import types
36 | import glm
37 | import sys
38 | import re
39 | import os
40 |
41 | import numpy as np
42 |
43 |
44 | LOGGING_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
45 |
46 |
47 | class StreamHandler(logging.StreamHandler):
48 | pass
49 |
50 |
51 | class LoggingWidget(logging.Handler):
52 | """ Custom logging handler sending logs to an output widget """
53 |
54 | def __init__(self, height='256px', *args, **kwargs):
55 | super(LoggingWidget, self).__init__(*args, **kwargs)
56 |
57 | self.out = ipywidgets.Output()
58 | self.set_layout(height)
59 |
60 | def set_layout(self, height='256px', overflow='scroll', **kwargs):
61 | self.out.layout=ipywidgets.Layout(
62 | height=height,
63 | overflow=overflow,
64 | **kwargs
65 | )
66 |
67 | def emit(self, record):
68 | with self.out:
69 | print(self.format(record))
70 |
71 |
72 | def get_logging_widget(height='256px', quiet_default_logger=True):
73 |
74 | if type(height) is int:
75 | height = str(height) + 'px'
76 |
77 | logger = logging.getLogger()
78 |
79 | wl = [h for h in logger.handlers if isinstance(h, LoggingWidget)]
80 | if wl:
81 | w = wl[-1]
82 | w.set_layout(height)
83 | return w.out
84 |
85 | handler = LoggingWidget(height)
86 | handler.setLevel(logging.DEBUG)
87 | handler.setFormatter(logging.Formatter(LOGGING_FORMAT))
88 |
89 | logger.addHandler(handler)
90 |
91 | if quiet_default_logger:
92 | wl = [h for h in logger.handlers if isinstance(h, StreamHandler)]
93 | if wl:
94 | wl[-1].setLevel(logging.ERROR)
95 |
96 | return handler.out
97 |
98 |
99 | _logging_level = logging.WARNING
100 |
101 |
102 | def get_logging_level():
103 | return _logging_level
104 |
105 |
106 | def setup_basic_logging(level):
107 | """Set up basic logging
108 |
109 | Args:
110 | level (int): The log level
111 | """
112 |
113 | global _logging_level
114 |
115 | if type(level) is str:
116 | level = logging._nameToLevel.get(level, None)
117 |
118 | if level is None:
119 | return
120 |
121 | _logging_level = level
122 |
123 | logger = logging.getLogger()
124 | logger.setLevel(level)
125 |
126 | if not logger.handlers:
127 |
128 | handler = StreamHandler()
129 | handler.setLevel(logging.DEBUG)
130 | handler.setFormatter(logging.Formatter(LOGGING_FORMAT))
131 |
132 | logger.addHandler(handler)
133 |
134 |
135 | def abspath(path):
136 |
137 | dirname = os.path.dirname(os.path.abspath(__file__))
138 | return os.path.abspath(os.path.join(dirname, path))
139 |
140 |
141 | def callerpath(levelsup=1):
142 |
143 | ff = inspect.currentframe().f_back
144 | for i in range(levelsup):
145 | ff = ff.f_back
146 |
147 | pp = ff.f_globals.get('__file__', '')
148 | return os.path.dirname(pp)
149 |
150 |
151 | def callerframe(levelsup=1):
152 |
153 | ff = inspect.currentframe().f_back
154 | for i in range(levelsup):
155 | ff = ff.f_back
156 |
157 | return ff
158 |
159 |
160 | def auto_read(s):
161 | return s if '\n' in s else open(s).read()
162 |
163 |
164 | def o2h(o, n=12):
165 | return hashlib.sha256(pickle.dumps(o)).hexdigest()[:n]
166 |
167 |
168 | class Dict(dict):
169 |
170 | def __dir__(self):
171 | return list(self.keys()) + super().__dir__()
172 |
173 | def __getattr__(self, k):
174 |
175 | if k not in self:
176 | raise AttributeError(k)
177 |
178 | return self[k]
179 |
180 | def __setattr__(self, k, v):
181 | self[k] = v
182 |
183 |
184 | def patch_method(obj, key, method):
185 |
186 | foo = getattr(obj, key)
187 |
188 | if isinstance(foo.__func__, functools.partial):
189 | return foo
190 |
191 | par = functools.partial(method, foo=foo)
192 | bar = types.MethodType(par, obj)
193 | bar.__func__.__name__ = foo.__func__.__name__
194 |
195 | setattr(obj, key, bar)
196 |
197 | return bar
198 |
199 |
200 | def glm_dumps(o):
201 |
202 | if "'glm." not in repr(o.__class__):
203 | return o
204 |
205 | return ('__glm__', o.__class__.__name__, tuple(o))
206 |
207 |
208 | def glm_loads(o):
209 |
210 | if type(o) is not tuple or not o or o[0] != '__glm__':
211 | return o
212 |
213 | return getattr(glm, o[1])(o[2])
214 |
215 |
216 | def trimmed_traceback():
217 |
218 | e = ''.join(traceback.format_exception(*sys.exc_info()))
219 | e = re.sub(r'(?s)^.*?The above exception was the direct cause of the following exception:\s*', '', e)
220 | return e
221 |
222 |
223 | def auto(o):
224 |
225 | t = type(o)
226 |
227 | if t in (tuple, list):
228 | return t(auto(v) for v in o)
229 |
230 | if t is dict:
231 | return {k: auto(v) for k, v in o.items()}
232 |
233 | if t is not str:
234 | return o
235 |
236 | if o.isdecimal():
237 | return int(o)
238 |
239 | try:
240 | return float(o)
241 | except:
242 | pass
243 |
244 | return o
245 |
246 |
247 | def settable(o, name):
248 |
249 | if name[0] == '_':
250 | return False
251 |
252 | if name in o.__dict__:
253 | return True
254 |
255 | v = getattr(o, name, '__NONE__')
256 |
257 | return v != '__NONE__' and not callable(v)
258 |
259 |
260 | def np_is_zero(a):
261 | return np.abs(a).sum().item() == 0
262 |
263 |
264 | class Enum(object):
265 |
266 | def __init__(self, **kwargs):
267 |
268 | for k, v in kwargs.items():
269 | setattr(self, k, v)
270 |
271 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 | license_files=LICENSE.txt
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | import setuptools
4 |
5 |
6 | with open('README.md', 'rb') as f:
7 | long_description = f.read().decode()
8 |
9 |
10 | setuptools.setup(
11 | name = 'jupylet',
12 | packages = ['jupylet', 'jupylet.audio'],
13 | package_data={
14 | 'jupylet': ['assets/*', 'assets/*/*', 'assets/*/*/*'],
15 | },
16 | version = '0.9.2',
17 | license='bsd-2-clause',
18 | description = 'Python game programming in Jupyter notebooks.',
19 | long_description=long_description,
20 | long_description_content_type="text/markdown",
21 | author = 'Nir Aides',
22 | author_email = 'nir.8bit@gmail.com',
23 | url = 'https://github.com/nir/jupylet',
24 | download_url = 'https://github.com/nir/jupylet/archive/v0.9.2.tar.gz',
25 | keywords = [
26 | 'reinforcement learning',
27 | 'deep learning',
28 | 'synthesizers',
29 | 'moderngl',
30 | 'children',
31 | 'jupyter',
32 | 'python',
33 | 'games',
34 | 'midi',
35 | 'kids',
36 | 'RL',
37 | ],
38 | python_requires='>=3.9,<3.13',
39 | install_requires=[
40 | 'glfw',
41 | 'mido',
42 | 'tqdm',
43 | 'jedi',
44 | 'numpy',
45 | 'PyGLM',
46 | 'scipy',
47 | 'pillow',
48 | 'gltflib',
49 | 'jupyter',
50 | 'notebook',
51 | 'moderngl',
52 | 'soundfile',
53 | 'webcolors',
54 | 'ipyevents',
55 | 'ipywidgets',
56 | 'matplotlib',
57 | 'sounddevice',
58 | 'soundcard; platform_system=="Darwin"',
59 | 'python-rtmidi',
60 | 'moderngl-window',
61 | ],
62 | extras_require = {
63 | 'midi': ['python-rtmidi']
64 | },
65 | classifiers=[
66 | 'Development Status :: 4 - Beta',
67 | 'Intended Audience :: Education',
68 | 'Intended Audience :: Developers',
69 | 'Intended Audience :: Science/Research',
70 | 'Topic :: Education',
71 | 'Topic :: Multimedia :: Graphics',
72 | 'Topic :: Multimedia :: Graphics :: 3D Rendering',
73 | 'Topic :: Multimedia :: Sound/Audio',
74 | 'Topic :: Multimedia :: Sound/Audio :: MIDI',
75 | 'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis',
76 | 'Topic :: Scientific/Engineering :: Artificial Intelligence',
77 | 'License :: OSI Approved :: BSD License',
78 | 'Programming Language :: Python :: 3.9',
79 | 'Programming Language :: Python :: 3.10',
80 | 'Programming Language :: Python :: 3.11',
81 | 'Programming Language :: Python :: 3.12',
82 | ],
83 | )
84 |
85 |
--------------------------------------------------------------------------------
| | |