diff --git a/.gitignore b/.gitignore index f90bb6b..8df3f08 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -out.wav \ No newline at end of file +out.wav +*/target/* +code-guessing/analytics/people \ No newline at end of file diff --git a/README.md b/README.md index 988eef1..ffbf48b 100644 --- a/README.md +++ b/README.md @@ -39,4 +39,14 @@ This comes with absolutely no guarantee of support or correct function, although * `length_terminated_strings.c` - a revolution in computer science, combining the efficient `strlen` of null-terminated strings with the... inclusion of length? of length-prefixed/fat-pointer strings. A length-terminated string has its length at the *end*, occupying 1 to 8 bytes. To find its length, simply traverse the string until the data at the end matches the length traversed so far. Yes, this implementation might slightly have a bit of undefined behaviour. * `discord-message-dump.py`, which reads a GDPR data dump from Discord and copies all the messages in public channels to a CSV file. I used this for training of a GPT-2 instance on my messages (available on request). * `spudnet-http.py` - connect to the SPUDNET backend underlying [PotatOS](https://git.osmarks.net/osmarks/potatOS/)'s ~~backdoors~~ remote debugging system via the convenient new HTTP long-polling-based API. -* `fractalart-rs` - [this](https://github.com/TomSmeets/FractalArt/) in Rust and faster, see its own README for more details. \ No newline at end of file +* `fractalart-rs` - [this](https://github.com/TomSmeets/FractalArt/) in Rust and faster, see its own README for more details. +* `arbtt_wayland_toplevel.py` - interfaces [arbtt](https://arbtt.nomeata.de/) with Wayland foreign toplevel protocol and idle notifications. +* `fractalize_image.py` - used for making a profile picture for someone once. +* `goose2function.py` - converts goose neck profiles extracted from images of geese into activation functions for machine learning. +* `histretention.py` - dump Firefox `places.sqlite3` into a separate database (Firefox clears out old history internally for performance reasons or something like that) for later search. +* `memeticize.py` - the script I use to process memes from a large directory of heterogenous files. +* `rng_trainer.html` - a very unfinished attempt to implement a paper I found on training high-quality random number generation. +* `smtp2rss.py` - bridge SMTP (inbound emails) to RSS. +* `yearbox.html` - DokuWiki-type yearbox prototype for Minoteaur (I think this actually contains an off-by-one error somewhere; it isn't what's actually in use). +* `arbitrary-politics-graphs` - all you need to run your own election campaign. +* `heavbiome` - some work on biome generation with Perlin noise. \ No newline at end of file diff --git a/arbitrary-politics-graphs/g1.png b/arbitrary-politics-graphs/g1.png new file mode 100644 index 0000000..a98576a Binary files /dev/null and b/arbitrary-politics-graphs/g1.png differ diff --git a/arbitrary-politics-graphs/g2.png b/arbitrary-politics-graphs/g2.png new file mode 100644 index 0000000..df481ba Binary files /dev/null and b/arbitrary-politics-graphs/g2.png differ diff --git a/arbitrary-politics-graphs/g3.png b/arbitrary-politics-graphs/g3.png new file mode 100644 index 0000000..61cc519 Binary files /dev/null and b/arbitrary-politics-graphs/g3.png differ diff --git a/arbitrary-politics-graphs/g4.png b/arbitrary-politics-graphs/g4.png new file mode 100644 index 0000000..fea2b21 Binary files /dev/null and b/arbitrary-politics-graphs/g4.png differ diff --git a/arbitrary-politics-graphs/test.html b/arbitrary-politics-graphs/test.html new file mode 100644 index 0000000..f0a2b8e --- /dev/null +++ b/arbitrary-politics-graphs/test.html @@ -0,0 +1,40 @@ + + +
+
+

if I am elected

+

good things

+ +

bad things

+ +
+
+

otherwise

+

good things

+ +

bad things

+ +
+
\ No newline at end of file diff --git a/arbtt_wayland_toplevel.py b/arbtt_wayland_toplevel.py new file mode 100755 index 0000000..e03d85a --- /dev/null +++ b/arbtt_wayland_toplevel.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +from functools import partial +from datetime import datetime, timezone +from wl_framework.network.connection import WaylandConnection +from wl_framework.protocols.base import UnsupportedProtocolError +from wl_framework.protocols.foreign_toplevel import ForeignTopLevel +from wl_framework.protocols.data_control import DataControl +from wl_framework.protocols.idle_notify import ( IdleNotifyManager, IdleNotifier as _IdleNotifier ) +import asyncio.subprocess as subprocess +import orjson + +class ForeignTopLevelMonitor(ForeignTopLevel): + def __init__(self, *args, **kwargs): + self.tlwindows = {} + super().__init__(*args, **kwargs) + + def on_toplevel_created(self, toplevel): + self.tlwindows[toplevel.obj_id] = {} + + def on_toplevel_synced(self, toplevel): + self.tlwindows[toplevel.obj_id]["app_id"] = toplevel.app_id + self.tlwindows[toplevel.obj_id]["title"] = toplevel.title + self.tlwindows[toplevel.obj_id]["states"] = toplevel.states + self.tlwindows[toplevel.obj_id]["outputs"] = { x.name for x in toplevel.outputs } + + def on_toplevel_closed(self, toplevel): + del self.tlwindows[toplevel.obj_id] + +# not actually using this +class ClipboardMonitor(DataControl): + + def on_new_selection(self, offer): + self._print_selection(offer) + self._receive(offer) + + def on_new_primary_selection(self, offer): + self._print_selection(offer, is_primary=True) + self._receive(offer, is_primary=True) + + # Internal + def _receive(self, offer, is_primary=False): + if offer is None: + return + for mime in ( + 'text/plain;charset=utf-8', + 'UTF8_STRING', + ): + if mime in offer.get_mime_types(): + offer.receive(mime, partial(self._on_received, is_primary=is_primary)) + break + + def _print_selection(self, offer, is_primary=False): + _selection = 'primary' if is_primary else 'main' + if offer is None: + self.log(f"{_selection.capitalize()} selection cleared") + return + self.log(f"New {_selection} selection offers:") + for mime_type in offer.get_mime_types(): + self.log(f" {mime_type}") + + def _on_received(self, mime_type, data, is_primary=False): + if data: + data = data.decode('utf-8') + self.log(f"Received {' primary' if is_primary else 'main'} selection: '{data}'") + +class IdleNotifier(_IdleNotifier): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.idle_start = None + + def on_idle(self): + self.idle_start = datetime.now(tz=timezone.utc) + + def on_resume(self): + self.idle_start = None + +class WlMonitor(WaylandConnection): + def on_initial_sync(self, data): + super().on_initial_sync(data) + self.toplevels = ForeignTopLevelMonitor(self) + #self.clipboard = ClipboardMonitor(self) + self.idle = IdleNotifyManager(self, IdleNotifier) + self.idle_notifier = self.idle.get_idle_notifier(0, self.display.seat) + +INTERVAL_MS = 60_000 + +def generate_log_entry(wl: WlMonitor()): + entry = {"desktop": ""} + now = datetime.now(timezone.utc) + entry["date"] = now.isoformat() + entry["rate"] = INTERVAL_MS + if wl.idle_notifier.idle_start: + entry["inactive"] = int((now.timestamp() - wl.idle_notifier.idle_start.timestamp()) * 1000) + else: + entry["inactive"] = 0 + def arbitrary_output(window): + s = window["outputs"] + try: + return s.pop() + except KeyError: + return "" + entry["windows"] = [ { "title": x["title"], "program": x["app_id"], "active": "activated" in x["states"], "hidden": "minimized" in x["states"] } for x in wl.toplevels.tlwindows.values() ] + return entry + +if __name__ == '__main__': + import sys + + import asyncio + from wl_framework.loop_integrations import AsyncIOIntegration + + async def init(): + arbtt_importer = await subprocess.create_subprocess_exec("arbtt-import", "-a", "-t", "JSON", stdin=subprocess.PIPE) + loop = AsyncIOIntegration() + try: + app = WlMonitor(eventloop_integration=loop) + await asyncio.sleep(1) + while True: + entry = generate_log_entry(app) + arbtt_importer.stdin.write(orjson.dumps(entry)) + arbtt_importer.stdin.write(b"\n") + await asyncio.sleep(INTERVAL_MS / 1000) + except RuntimeError as e: + print(e) + sys.exit(1) + try: + asyncio.run(init()) + except KeyboardInterrupt: + print() diff --git a/beep.py b/beep.py index caa2b87..cb6b4b5 100644 --- a/beep.py +++ b/beep.py @@ -56,10 +56,27 @@ def save_wav(file_name): wav_file.close() return +BAD_INTERVALS = [6, 8, 10, 11, 13] +NOTE_FACTOR = pow(2, 1/12) +LOG_NOTE_FACTOR = math.log(NOTE_FACTOR) +BASE = 440 +MIN = math.ceil(math.log(200/440) / LOG_NOTE_FACTOR) +MAX = math.floor(math.log(2000/440) / LOG_NOTE_FACTOR) + import random -freq = 6 -for i in range(160): - append_sinewave(volume=1.0, freq=math.exp(freq), duration_milliseconds=50) - freq += random.uniform(-0.2, 0.2) - freq = max(4.5, min(freq, 9)) +for i in range(10): + """ + for i in range(3): + append_sinewave(800, 100) + append_silence(100) + append_sinewave(600, 500) + append_silence() + """ + note = random.randint(MIN, MAX) + while MIN <= note <= MAX: + interval = random.choice(BAD_INTERVALS) + time = random.randint(50, 1000) + append_sinewave(BASE * pow(NOTE_FACTOR, note), time) + note += interval if random.choice((True, False)) else -interval + save_wav("output.wav") diff --git a/code-guessing/analytics/download_6_to_13.py b/code-guessing/analytics/download_6_to_13.py new file mode 100644 index 0000000..2865f21 --- /dev/null +++ b/code-guessing/analytics/download_6_to_13.py @@ -0,0 +1,28 @@ +from bs4 import BeautifulSoup +import requests +import os +import os.path + +people = { + +} + +for round_id in range(6, 13): + print("round", round_id) + m = BeautifulSoup(requests.get(f"https://cg.esolangs.gay/{round_id}/").text, features="lxml") + for file_link in m.select("details > summary > a"): + name = file_link.parent.parent.previous_sibling.previous_sibling.previous_sibling.previous_sibling + if "impersonating" in name.text: + name = name.previous_sibling.previous_sibling + assert "written by" in name.text, f"oh no {name.text}" + name = name.text.split("written by ")[-1] + name = people.get(name, name).lower() + href = file_link.attrs["href"] + filename = str(round_id) + "-" + href.split(f"/{round_id}/")[-1] + full_href = f"https://cg.esolangs.gay{href}" + os.makedirs(os.path.join("people", name), exist_ok=True) + with open(os.path.join("people", name, filename), "wb") as f: + with requests.get(full_href, stream=True) as r: + r.raise_for_status() + for chunk in r.iter_content(chunk_size=(2<<18)): + f.write(chunk) \ No newline at end of file diff --git a/code-guessing/analytics/run.py b/code-guessing/analytics/run.py new file mode 100644 index 0000000..7ad011e --- /dev/null +++ b/code-guessing/analytics/run.py @@ -0,0 +1,6 @@ +import os +import os.path + +for person in os.listdir("people"): + for submission in os.listdir(os.path.join("people", person)): + print(person, submission) \ No newline at end of file diff --git a/code-guessing/entry-rs/Cargo.lock b/code-guessing/entry-rs/Cargo.lock new file mode 100644 index 0000000..0b6b239 --- /dev/null +++ b/code-guessing/entry-rs/Cargo.lock @@ -0,0 +1,257 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "entry-rs" +version = "0.1.0" +dependencies = [ + "pyo3", +] + +[[package]] +name = "indoc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" +dependencies = [ + "indoc-impl", + "proc-macro-hack", +] + +[[package]] +name = "indoc-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", + "unindent", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "libc" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" +dependencies = [ + "paste-impl", + "proc-macro-hack", +] + +[[package]] +name = "paste-impl" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" +dependencies = [ + "proc-macro-hack", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pyo3" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35100f9347670a566a67aa623369293703322bb9db77d99d7df7313b575ae0c8" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "parking_lot", + "paste", + "pyo3-build-config", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d12961738cacbd7f91b7c43bc25cfeeaa2698ad07a04b3be0aa88b950865738f" +dependencies = [ + "once_cell", +] + +[[package]] +name = "pyo3-macros" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0bc5215d704824dfddddc03f93cb572e1155c68b6761c37005e1c288808ea8" +dependencies = [ + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71623fc593224afaab918aa3afcaf86ed2f43d34f6afde7f3922608f253240df" +dependencies = [ + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unindent" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/code-guessing/entry-rs/Cargo.toml b/code-guessing/entry-rs/Cargo.toml new file mode 100644 index 0000000..5db3d34 --- /dev/null +++ b/code-guessing/entry-rs/Cargo.toml @@ -0,0 +1,13 @@ + +[package] +name = "entry-rs" +version = "0.1.0" +edition = "2021" + +[lib] +name = "entry_rs" +crate-type = ["cdylib"] + +[dependencies.pyo3] +version = "0.14.5" +features = ["extension-module"] diff --git a/code-guessing/entry-rs/src/entry_impl.rs b/code-guessing/entry-rs/src/entry_impl.rs new file mode 100644 index 0000000..11b0b54 --- /dev/null +++ b/code-guessing/entry-rs/src/entry_impl.rs @@ -0,0 +1,85 @@ +pub fn entry(s: &str) -> i32 { + let digit = |c: u8| (c as i32) - 48; + let mut acc = 0; + let mut pos = 0; + let b = s.as_bytes(); + loop { + match b[pos] { + b'+' => { + acc += { + pos += 1; + let mut acc = 0; + while (pos + 1) < b.len() && (b[pos + 1] == b'/' || b[pos + 1] == b'*') { + println!("DIV or MUL {} {} {}", b[pos], b[pos + 1], b[pos + 2]); + if acc == 0 { + acc = digit(b[pos]) + } + if b[pos + 1] == b'/' { + acc /= digit(b[pos + 2]) + } else { + acc *= digit(b[pos + 2]) + } + pos += 2; + } + if acc == 0 { + digit(b[pos]) + } else { + acc + } + }; + pos += 1; + }, + b'-' => { + acc -= { + pos += 1; + let mut acc = -1; + while (pos + 1) < b.len() && (b[pos + 1] == b'/' || b[pos + 1] == b'*') { + println!("DIV or MUL {} {} {}", b[pos], b[pos + 1], b[pos + 2]); + if acc == -1 { + acc = digit(b[pos]) + } + if b[pos + 1] == b'/' { + acc /= digit(b[pos + 2]) + } else { + acc *= digit(b[pos + 2]) + } + pos += 2; + } + if acc == -1 { + digit(b[pos]) + } else { + acc + } + }; + pos += 1; + }, + x => { + acc += { + let mut acc = 0; + while (pos + 1) < b.len() && (b[pos + 1] == b'/' || b[pos + 1] == b'*') { + println!("DIV or MUL {} {} {}", b[pos], b[pos + 1], b[pos + 2]); + if acc == 0 { + acc = digit(b[pos]) + } + if b[pos + 1] == b'/' { + acc /= digit(b[pos + 2]) + } else { + acc *= digit(b[pos + 2]) + } + pos += 2; + } + if acc == 0 { + digit(b[pos]) + } else { + acc + } + }; + pos += 1 + } + } + if pos >= b.len() { + break + } + } + acc +} \ No newline at end of file diff --git a/code-guessing/entry-rs/src/lib.rs b/code-guessing/entry-rs/src/lib.rs new file mode 100644 index 0000000..5d5f084 --- /dev/null +++ b/code-guessing/entry-rs/src/lib.rs @@ -0,0 +1,16 @@ + +use pyo3::prelude::*; + +mod entry_impl; +use entry_impl::entry; + +#[pyfunction] +fn wrapped_entry(s: &str) -> PyResult { + Ok(entry(s)) +} + +#[pymodule] +fn entry_rs(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_function(wrap_pyfunction!(wrapped_entry, m)?)?; + Ok(()) +} diff --git a/code-guessing/gif.s b/code-guessing/gif.s new file mode 100644 index 0000000..2cc2eb7 --- /dev/null +++ b/code-guessing/gif.s @@ -0,0 +1,53 @@ +; a hand-made GIF containing valid JavaScript code +; abusing header to start a JavaScript comment + +; inspired by Saumil Shah's Deadly Pixels presentation + +; Ange Albertini, BSD Licence 2013 + +; yamal gifjs.asm -o img.gif + +WIDTH equ 10799 ; equivalent to 2f2a, which is '/*' in ASCII, thus starting an opening comment + +HEIGHT equ 10799 ; just to make it easier to spot + +db 'GIF89a' + dw WIDTH, HEIGHT + +db 0 ; GCT + db -1 ; background color + db 0 ; default aspect ratio + ;db 0fch, 0feh, 0fch + ;times COLORS db 0, 0, 0 + +; no need of Graphic Control Extension + ; db 21h, 0f9h + ; db GCESIZE ; size + ; gce_start: + ; db 0 ; transparent background + ; dw 0 ; delay for anim + ; db 0 ; other transparent + ; GCESIZE equ $ - gce_start + ; db 0 ; end of GCE + +db 02ch ; Image descriptor + dw 0, 0 ; NW corner + dw WIDTH, HEIGHT ; w/h of image + db 0 ; color table + +db 2 ; lzw size + +;db DATASIZE +;data_start: +; db 00, 01, 04, 04 +; DATASIZE equ $ - data_start + +db 0 +db 3bh ; GIF terminator + +; end of the GIF + +db '*/' ; closing the comment +db '=1;' ; creating a fake use of that GIF89a string + +db 'alert("haxx");' \ No newline at end of file diff --git a/code-guessing/giftest.html b/code-guessing/giftest.html new file mode 100644 index 0000000..82b4337 --- /dev/null +++ b/code-guessing/giftest.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/code-guessing/img.gif b/code-guessing/img.gif new file mode 100644 index 0000000..0672599 Binary files /dev/null and b/code-guessing/img.gif differ diff --git a/code-guessing/img.js b/code-guessing/img.js new file mode 120000 index 0000000..8322fc7 --- /dev/null +++ b/code-guessing/img.js @@ -0,0 +1 @@ +img.gif \ No newline at end of file diff --git a/code-guessing/test12.py b/code-guessing/test12.py new file mode 100644 index 0000000..f987964 --- /dev/null +++ b/code-guessing/test12.py @@ -0,0 +1,185 @@ +import sys +import importlib +import subprocess +import ctypes +import random +import traceback +import textwrap +import json +import os +import shutil +try: + from tqdm import tqdm, trange +except ImportError: + print("`tqdm` not found. there will be no progress bars") + def tqdm(x): + return x + trange = range + + +filename = sys.argv[1] + +if filename.endswith(".py"): + print("importing as Python...") + module = importlib.import_module(filename.removesuffix(".py")) + print("done.") + try: + entry = module.entry + except AttributeError: + print("module is missing entrypoint `entry`. aborting.") + sys.exit(1) +elif filename.endswith(".c"): + print("compiling as C with `gcc`...") + obj = "./" + filename.removesuffix(".c") + ".so" + rc = subprocess.call(["gcc", "-shared", "-fPIC", *sys.argv[2:], filename, "-o", obj]) + if rc != 0: + print("compilation failed. aborting.") + sys.exit(rc) + lib = ctypes.CDLL(obj) + try: + entry = lib.entry + except AttributeError: + print("library is missing entrypoint `entry`. aborting.") + sys.exit(1) +elif filename.endswith(".rs"): + print("compiling as Rust...") + os.makedirs("./entry-rs/src", exist_ok=True) + with open("./entry-rs/Cargo.toml", "w") as f: + f.write(""" +[package] +name = "entry-rs" +version = "0.1.0" +edition = "2021" + +[lib] +name = "entry_rs" +crate-type = ["cdylib"] + +[dependencies.pyo3] +version = "0.14.5" +features = ["extension-module"] +""") + with open("./entry-rs/src/lib.rs", "w") as f: + f.write(""" +use pyo3::prelude::*; + +mod entry_impl; +use entry_impl::entry; + +#[pyfunction] +fn wrapped_entry(s: &str) -> PyResult { + Ok(entry(s)) +} + +#[pymodule] +fn entry_rs(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_function(wrap_pyfunction!(wrapped_entry, m)?)?; + Ok(()) +} +""") + shutil.copyfile(filename, "./entry-rs/src/entry_impl.rs") + os.chdir("entry-rs") + rc = subprocess.call(["cargo", "build", "--release"]) + os.chdir("..") + if rc != 0: + print("compilation failed. aborting.") + sys.exit(1) + sys.path.append("./entry-rs/target/release") + os.rename("./entry-rs/target/release/libentry_rs.so", "./entry-rs/target/release/entry_rs.so") + module = importlib.import_module("entry_rs") + entry = module.wrapped_entry +else: + print("unrecognized file extension") + sys.exit(1) + + +tests = [ + ("zero", "0", 0), + ("add", "1+2", 3), + ("sub", "2-1", 1), + ("mul", "2*3", 6), + ("div", "4/2", 2), + ("floor", "4/3", 1), + ("neg", "0-4", -4), + ("chain", "4/2/2", 1), + ("olivia", "1+7/4-0*4", 2), + ("gollario", "4-4-4-4+8", 0) +] + +print("beginning testing suite") +failures = 0 + +for test_name, value, result in tqdm(tests): + print(f"`{test_name}`... ", end="") + try: + r = entry(value) + except BaseException: + print("error") + traceback.print_exc() + print(f"in test `{test_name}`:") + print(textwrap.indent(value, " "*2)) + failures += 1 + continue + if r == result: + print("ok") + else: + failures += 1 + print("failed") + print(f"for test `{test_name}`:") + print(textwrap.indent(value, " "*2)) + print(f"entry returned {r} when {result} was expected\n") +if not failures: + print("test suite finished, all ok") +else: + print(f"test suite finished. {failures} tests failed\n\n") + print("skipping randomized testing because your program is clearly broken and the output from those isn't very helpful for finding bugs") + sys.exit(2) + + +def gen_random_json(): + s = random.choice("0123456789") + for _ in range(random.randint(0, 10)): + s += random.choice("+-*/") + s += random.choice("0123456789") + return s + + +print("beginning randomized testing.") +random_failures = 0 +for _ in trange(100): + while True: + j = gen_random_json() + try: + tr = eval(j.replace("/", "//")) + except ZeroDivisionError: + continue + else: + break + try: + r = entry(j) + except BaseException: + print("error") + traceback.print_exc() + print("in randomized test case:") + print(textwrap.indent(j, " "*2)) + random_failures += 1 + continue + if r != tr: + print("randomized test case failed:") + print(textwrap.indent(j, " "*2)) + print(f"entry returned {r} when {tr} was expected\n") + random_failures += 1 + +if not random_failures: + print("randomized testing finished. all ok\n\n") +else: + print(f"randomized testing finished with {random_failures} failures\n\n") + + +print("overall report:") +overall = failures + random_failures +if not overall: + print("no failures detected. all seems well!") +else: + print(f"{overall} failures detected overall. you have some bugs to fix") + sys.exit(2) diff --git a/count_words_in_directory.py b/count_words_in_directory.py new file mode 100644 index 0000000..03937ca --- /dev/null +++ b/count_words_in_directory.py @@ -0,0 +1,14 @@ +import os, sys, string + +count = 0 +words = 0 +characters = 0 +for dirpath, dirnames, filenames in os.walk(sys.argv[1]): + for filename in filenames: + with open(os.path.join(dirpath, filename)) as f: + content = f.read() + characters += len([c for c in content if c in string.ascii_letters]) + words += len([thing for thing in content.split() if thing.strip()]) + count += 1 + +print(words / count, characters / count) \ No newline at end of file diff --git a/cycdec.py b/cycdec.py new file mode 100644 index 0000000..0638065 --- /dev/null +++ b/cycdec.py @@ -0,0 +1,19 @@ +perm = [ + [1, 2, 3, 4, 5, 6, 7, 8], + [5, 7, 8, 2, 3, 6, 4, 1] +] +perm = dict(zip(*perm)) +vals = set(perm) + +print(perm) +cycs = [] +while vals: + nxt = vals.pop() + seen = [] + while nxt not in seen: + lnxt = nxt + nxt = perm[nxt] + seen.append(lnxt) + cycs.append(seen) + vals -= set(seen) +print(cycs) \ No newline at end of file diff --git a/cyclcm.py b/cyclcm.py new file mode 100644 index 0000000..c62dc9f --- /dev/null +++ b/cyclcm.py @@ -0,0 +1,25 @@ +import math, itertools + +count = 0 +for permutation in itertools.permutations(range(7)): + perm = [ + list(range(7)), + list(permutation) + ] + perm = dict(zip(*perm)) + vals = set(perm) + + print(perm) + cycs = [] + while vals: + nxt = vals.pop() + seen = [] + while nxt not in seen: + lnxt = nxt + nxt = perm[nxt] + seen.append(lnxt) + cycs.append(seen) + vals -= set(seen) + if math.lcm(*map(len, cycs)) == 4: + count += 1 +print(count) \ No newline at end of file diff --git a/esolangs-iceberg/iceberg.html b/esolangs-iceberg/iceberg.html index 2fe270a..5b930c7 100644 --- a/esolangs-iceberg/iceberg.html +++ b/esolangs-iceberg/iceberg.html @@ -148,6 +148,7 @@ li, h2 {
  • heavpoot is osmarks
  • second #staff-only channel
  • colin
  • +
  • hactar secretly planning takeover
  • less than 10% of users are frequently active
  • cyber PC diagram
  • @@ -175,4 +176,4 @@ li, h2 { - \ No newline at end of file + diff --git a/fractalize_image.py b/fractalize_image.py new file mode 100644 index 0000000..649f99a --- /dev/null +++ b/fractalize_image.py @@ -0,0 +1,41 @@ +from PIL import Image +from functools import cache +from collections import namedtuple +Region = namedtuple("Region", ["left", "upper", "right", "lower"]) +glasses = Image.open("./pv.webp") +glasses = glasses.crop(glasses.getbbox()) +def scale(i, x): + if i.size[0] // x == 0 or i.size[1] // x == 0: + return False + return i.resize((int(i.size[0] // x), int(i.size[1] // x))) +output = Image.new("RGBA", (256, 256)) +def paste_at_centre(src: Image, x, y): + #dc = Region(*dc) + left = x - src.size[0] // 2 + upper = y - src.size[1] // 2 + output.alpha_composite(src, (left, upper)) + +paste_at_centre(glasses, output.size[0] // 2, output.size[1] // 2) +def do_transpositions(im, parity, count): + if count == 0: return im + if parity: + return do_transpositions(im.transpose(Image.Transpose.ROTATE_90), parity, count - 1) + else: + return do_transpositions(im.transpose(Image.Transpose.ROTATE_270), parity, count - 1) +k = 1 +while True: + #output.show() + s = 2**k + bounds = Region(*output.getbbox()) + print(bounds) + if not scale(output, s): + break + g = do_transpositions(scale(output, s), True, k) + h = do_transpositions(scale(output, s), False, k) + paste_at_centre(g, bounds.left, bounds.upper) + paste_at_centre(h, bounds.right, bounds.upper) + paste_at_centre(g, bounds.left, bounds.lower) + paste_at_centre(h, bounds.right, bounds.lower) + k += 1 +output.show() +output.save("./pvf.webp") \ No newline at end of file diff --git a/goose2function.py b/goose2function.py new file mode 100644 index 0000000..6c5e9a2 --- /dev/null +++ b/goose2function.py @@ -0,0 +1,31 @@ +import cv2, numpy, matplotlib.pyplot as plt, scipy.interpolate, sys +from collections import defaultdict + +img = cv2.imread(sys.argv[1]) +H, S, L = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV)) +img_blur = cv2.GaussianBlur(H, (15,15), 0) +thresh = cv2.threshold(img_blur, 55, 255, cv2.THRESH_BINARY)[1] +edges = cv2.Canny(image=thresh, threshold1=100, threshold2=200) +c, hier = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) +best = max(c, key=lambda x: x.shape[0])[:, 0, :] +min_x, max_x = min(best[:, 0]), max(best[:, 0]) +min_y, max_y = min(best[:, 1]), max(best[:, 1]) +xrange = max_x - min_x +yrange = max_y - min_y +coords = [[((x - min_x) / xrange) * 2 - 1, ((y - min_y) / yrange) * 2 - 1] for x, y in best ] +coords.sort(key=lambda x: x[0]) +coords2 = defaultdict(list) +for x, y in coords: + coords2[x].append(y) +coords3x = [] +coords3y = [] +last = -1 +for x, ys in coords2.items(): + coords3y.append(min(ys, key=lambda x: abs(x - last))) + coords3x.append(x) + last = coords3y[-1] +i = scipy.interpolate.CubicSpline(coords3x, coords3y, extrapolate=True) +xs = numpy.arange(-1.0, 1.0, 0.02) +plt.plot(coords3x, coords3y, label='data') +plt.plot(xs, i(xs)) +plt.show() \ No newline at end of file diff --git a/heavbiome/main.lua b/heavbiome/main.lua new file mode 100644 index 0000000..45fd217 --- /dev/null +++ b/heavbiome/main.lua @@ -0,0 +1,93 @@ +local map = {} +local w = 300 +local h = 300 +local seed = 349259 +require("perlin") -- not mine. +-- it uses globals. spiteful, but i do not care enough about code quality for this. + + +local biomecols = { + empty = {0.5, 0.5, 0.5}, + forest = { 0, 0.5, 0}, + rainforest = { 0, 0.5, 0.5}, + ocean = { 0, 0, 1}, + plains = { 0, 1, 0}, + desert = { 1, 1, 0}, +} + +local PERSISTENCE = 0.3 +local ITERS = 4 +local function oct_noise(x, y, seed, gen) + local total = 0 + local frequency = 1 + local amplitude = 1 + local norm = 0 + for i = 1, ITERS do + total = total + gen:noise(x * frequency, y * frequency, seed + i) * amplitude + norm = norm + amplitude + frequency = frequency * 2 + amplitude = amplitude * PERSISTENCE + end + return total +end + +local sf = 1/20 -- scaling factor. +local function pick_biome(x, y, gen) + local islandicity = gen:noise(x*sf/10, y*sf/10, 302382359)/2+1/2 + islandicity = math.min(math.max(0,islandicity*3),1) + local mainland_modifier = gen:noise(x*sf/20, y*sf/20, 302382359)/2 + local is_ocean = gen:noise(x*sf/4, y*sf/4, 555575)/2+1/2 + gen:noise(x*sf, y*sf, 555575)/(25-islandicity*10) + mainland_modifier + local humidity = oct_noise(x*sf/2, y*sf/2, 10000, gen)/2+1/2 + local temp = oct_noise(x*sf/6, y*sf/6, 20000, gen)/2+1/2 + --return { is_ocean, humidity, temp } + -- + if is_ocean > 0.45 then return "ocean" end + if is_ocean > 0.43 and humidity < 0.6 then return "desert" end -- coast? + if temp > 0.75 then return "desert" end + if humidity < 0.5 then + if temp > 0.5 then + return "desert" + else + return "plains" + end + else + if humidity > 0.75 then + return "rainforest" + else + if temp < 0.6 then + return "forest" + else + return "plains" + end + end + end + return "empty" + --]] +end + +function love.load() + local noise = perlin(seed) + local gen = noise + for x=1, w do + map[x] = {} + for y=1, h do + map[x][y] = 0 + end + end + for x=1, w do + for y=1, h do + local biome = pick_biome(x, y, noise) + --map[x][y] = biome + map[x][y] = biomecols[biome] + end + end +end + +function love.draw() + for x=1, w do + for y=1, h do + love.graphics.setColor(unpack(map[x][y])) + love.graphics.rectangle("fill",x*2, y*2, 2, 2) + end + end +end \ No newline at end of file diff --git a/heavbiome/perlin.lua b/heavbiome/perlin.lua new file mode 100644 index 0000000..56c0538 --- /dev/null +++ b/heavbiome/perlin.lua @@ -0,0 +1,134 @@ +local defaultSeed = 1337 + +local dot_product = { + [0x0]=function(x,y,z) return x + y end, + [0x1]=function(x,y,z) return -x + y end, + [0x2]=function(x,y,z) return x - y end, + [0x3]=function(x,y,z) return -x - y end, + [0x4]=function(x,y,z) return x + z end, + [0x5]=function(x,y,z) return -x + z end, + [0x6]=function(x,y,z) return x - z end, + [0x7]=function(x,y,z) return -x - z end, + [0x8]=function(x,y,z) return y + z end, + [0x9]=function(x,y,z) return -y + z end, + [0xA]=function(x,y,z) return y - z end, + [0xB]=function(x,y,z) return -y - z end, + [0xC]=function(x,y,z) return y + x end, + [0xD]=function(x,y,z) return -y + z end, + [0xE]=function(x,y,z) return y - x end, + [0xF]=function(x,y,z) return -y - z end +} + +local grad = function(hash, x,y,z) + return dot_product[hash % 0x10](x,y,z) +end + +local fade = function(t) + return t * t * t * (t * (t * 6 - 15) + 10) +end + +local lerp = function(t,a,b) + return a + t * (b - a) +end + +local generatePermutation = function(seed) + math.randomseed(seed) + + local permutation = {0} + + for i=1,255 do + table.insert(permutation,math.random(1,#permutation+1),i) + end + + local p = {} + + for i=0,255 do + p[i] = permutation[i+1] + p[i+256] = permutation[i+1] + end + + return p +end + +perlin = {} +perlin.__index = perlin + +perlin.noise = function(self,x,y,z) + y = y or 0 + z = z or 0 + + local xi = math.floor(x) % 0x100 + local yi = math.floor(y) % 0x100 + local zi = math.floor(z) % 0x100 + + x = x - math.floor(x) + y = y - math.floor(y) + z = z - math.floor(z) + + local u = fade(x) + local v = fade(y) + local w = fade(z) + + local A, AA, AB, AAA, ABA, AAB, ABB, B, BA, BB, BAA, BBA, BAB, BBB + A = self.p[xi ] + yi + AA = self.p[A ] + zi + AB = self.p[A+1 ] + zi + AAA = self.p[ AA ] + ABA = self.p[ AB ] + AAB = self.p[ AA+1 ] + ABB = self.p[ AB+1 ] + + B = self.p[xi+1] + yi + BA = self.p[B ] + zi + BB = self.p[B+1 ] + zi + BAA = self.p[ BA ] + BBA = self.p[ BB ] + BAB = self.p[ BA+1 ] + BBB = self.p[ BB+1 ] + + return lerp(w, + lerp(v, + lerp(u, + grad(AAA,x,y,z), + grad(BAA,x-1,y,z) + ), + lerp(u, + grad(ABA,x,y-1,z), + grad(BBA,x-1,y-1,z) + ) + ), + lerp(v, + lerp(u, + grad(AAB,x,y,z-1), grad(BAB,x-1,y,z-1) + ), + lerp(u, + grad(ABB,x,y-1,z-1), grad(BBB,x-1,y-1,z-1) + ) + ) + ) +end + +setmetatable(perlin,{ + __call = function(self,seed) + seed = seed or defaultSeed + + return setmetatable({ + seed = seed, + p = generatePermutation(seed), + },self) + end +}) + +--[[ +EXAMPLE + local p1 = perlin(1338) + local p2 = perlin(1337) + + local x,y,z = 1.0,2.0,3.0 + + print(p1:noise(x,y,z)) + print(p2:noise(x,y,z)) + + >> 0.23456 + >> 0.47598 +--]] \ No newline at end of file diff --git a/histretention.py b/histretention.py new file mode 100644 index 0000000..befec85 --- /dev/null +++ b/histretention.py @@ -0,0 +1,36 @@ +import sqlite3 + +longterm = sqlite3.connect("/data/archive/lthist.sqlite3") +longterm.executescript(f""" +CREATE TABLE IF NOT EXISTS places ( + guid TEXT PRIMARY KEY, + url TEXT, + title TEXT, + visit_count INTEGER DEFAULT 0, + last_visit_date INTEGER, + description TEXT, + preview_image_url TEXT +); +CREATE TABLE IF NOT EXISTS bookmarks ( + guid TEXT PRIMARY KEY, + bookmark TEXT NOT NULL REFERENCES places(guid), + title TEXT, + dateAdded INTEGER, + lastModified INTEGER +); +CREATE TABLE IF NOT EXISTS historyvisits ( + id TEXT PRIMARY KEY, + place TEXT NOT NULL REFERENCES places(guid), + date INTEGER NOT NULL, + type INTEGER NOT NULL +); +""") +longterm.execute("ATTACH DATABASE '/tmp/places.sqlite' AS transient;") +longterm.execute("""INSERT INTO places SELECT guid, url, title, visit_count, last_visit_date, description, preview_image_url FROM moz_places WHERE true +ON CONFLICT DO UPDATE SET visit_count = excluded.visit_count, last_visit_date = excluded.last_visit_date, title = excluded.title, description = excluded.description, preview_image_url = excluded.preview_image_url;""") +longterm.execute("""INSERT INTO bookmarks SELECT moz_bookmarks.guid, moz_places.guid, moz_bookmarks.title, dateAdded, lastModified FROM moz_bookmarks JOIN moz_places ON moz_places.id = moz_bookmarks.fk WHERE true +ON CONFLICT DO UPDATE SET lastModified = excluded.lastModified, title = excluded.title;""") +# TODO: possibly wrong with new profile, might need to increment historyvisits or something +longterm.execute("INSERT INTO historyvisits SELECT (moz_historyvisits.id || '/' || visit_date), moz_places.guid, visit_date, visit_type FROM moz_historyvisits JOIN moz_places ON moz_places.id = moz_historyvisits.place_id ON CONFLICT DO NOTHING;") + +longterm.commit() \ No newline at end of file diff --git a/hyperplane.py b/hyperplane.py new file mode 100644 index 0000000..72277b3 --- /dev/null +++ b/hyperplane.py @@ -0,0 +1,25 @@ +def step(x): + return ((x * 1039) + (x * 6177) + 1605 + (x * 4253)) % 8041 + +points = set() +a, b, c = None, None, 417 +for n in range(100000): + a, b, c = b, c, step(c) + if a is not None and b is not None: + points.add((a, b, c)) + +import numpy as np +import matplotlib.pyplot as plt + +fig = plt.figure() +ax = plt.axes(projection="3d") +def unzip(l): + ls = [] + for x in l: + for i, v in enumerate(x): + if len(ls) <= i: + ls.append([]) + ls[i].append(v) + return ls +ax.scatter(*unzip(points)) +plt.show() \ No newline at end of file diff --git a/memeticize.py b/memeticize.py new file mode 100644 index 0000000..0c3e86a --- /dev/null +++ b/memeticize.py @@ -0,0 +1,22 @@ +import os, sys, subprocess, datetime + +dt_threshold = datetime.datetime(2022, 11, 27).timestamp() + +_, indir, outdir = sys.argv +for x in os.listdir(indir): + inpath = os.path.join(indir, x) + if os.stat(inpath).st_mtime > dt_threshold: + if subprocess.run(("feh", inpath)).returncode == 0: + newname = input(x + ": ") + if newname: + ctr = 1 + while True: + newpath = os.path.join(outdir, newname + (f"-{ctr}" if ctr > 1 else "") + os.path.splitext(x)[1]) + if os.path.exists(newpath): + print("already in use") + ctr += 1 + else: + os.rename(inpath, newpath) + break + else: + print("keeping") \ No newline at end of file diff --git a/output.wav b/output.wav new file mode 100644 index 0000000..912d8bd Binary files /dev/null and b/output.wav differ diff --git a/randomlookingthing.html b/randomlookingthing.html new file mode 100644 index 0000000..479a0a0 --- /dev/null +++ b/randomlookingthing.html @@ -0,0 +1,61 @@ + +

    changed version

    + + + + +
    + + \ No newline at end of file diff --git a/rank_ordering_assignment_system.html b/rank_ordering_assignment_system.html new file mode 100644 index 0000000..68db81c --- /dev/null +++ b/rank_ordering_assignment_system.html @@ -0,0 +1,116 @@ + + + + \ No newline at end of file diff --git a/rng_trainer.html b/rng_trainer.html new file mode 100644 index 0000000..97ba35c --- /dev/null +++ b/rng_trainer.html @@ -0,0 +1,61 @@ + + + + + +
    + + +
    +
    +
    + +
    +
    + \ No newline at end of file diff --git a/smtp2rss.py b/smtp2rss.py new file mode 100644 index 0000000..7652fbe --- /dev/null +++ b/smtp2rss.py @@ -0,0 +1,175 @@ +import asyncio +from aiosmtpd.controller import UnthreadedController +from aiosmtpd.smtp import SMTP, syntax +from email.message import Message, EmailMessage +from email import message_from_bytes +from email.header import Header, decode_header, make_header +import aiosqlite +from datetime import datetime, timezone +from aiohttp import web +import re +import json +import feedparser.sanitizer +import rfeed +import base64 + +def now(): return datetime.now(tz=timezone.utc) +def decode_mime(subject): return str(make_header(decode_header(subject))) + +def handle_addr(a): + if a: + if x := re.search("<(.*)>$", a.strip()): + return x.group(1) + else: + return a.strip() + +async def open_connection(): + conn = await aiosqlite.connect("./smtp2rss.sqlite3") + conn.row_factory = aiosqlite.Row + await conn.execute("PRAGMA journal_mode = WAL") + await conn.executescript(""" +CREATE TABLE IF NOT EXISTS mails ( + id INTEGER PRIMARY KEY, + timestamp REAL NOT NULL, + full_mail BLOB NOT NULL, + from_addr TEXT, + to_addr TEXT, + subject TEXT +); + """) + await conn.commit() + return conn + +routes = web.RouteTableDef() + +import dominate +from dominate.tags import * + +def base_template(title, content, err=None): + doc = dominate.document(title=title) + + with doc.head: + meta(name="viewport", content="width=device-width, initial-scale=1.0") + style(""" +* { + box-sizing: border-box; +} + +h1, h2, h3 { + margin-top: 0; + border-bottom: 1px solid gray; + font-weight: normal; +} + +.mails .entry { + border: 1px solid gray; + margin: 0.5em; + padding: 0.5em; +} +""") + with doc: + if err: div(err, cls="error") + h1(title, cls="title") + m = main() + m += content + + return web.Response(text=doc.render(), content_type="text/html") + +preference = { + "text/html": 2, + "text/plain": 1 +} + +def email_to_html(emsg, debug_info=False): + if isinstance(emsg, Message): + payload = emsg.get_payload() + if isinstance(payload, list): + if not debug_info and emsg.get_content_type() == "multipart/alternative": + payload.sort(key=lambda x: preference.get(x.get_content_type(), 0)) + return email_to_html(payload[-1], debug_info) + else: + html = [ email_to_html(thing, debug_info) for thing in payload ] + else: + try: + payload = emsg.get_payload(decode=True).decode("utf-8") + except: + payload = emsg.get_payload(decode=True).decode("latin1") + if emsg.get_content_subtype() == "html": + html = div(dominate.util.raw(feedparser.sanitizer._sanitize_html(payload.replace("", ""), "utf-8", "text/html"))) + else: + html = pre(payload) + else: + html = [ email_to_html(thing, debug_info) for thing in emsg.get_body(list(preference.keys())) ] + return div([ + pre([ f"{header}: {value}\n" for header, value in emsg.items() ]) if debug_info else "", + html + ], cls="entry") + +async def run(): + accessed_feeds = {} + + loop = asyncio.get_event_loop() + db = await open_connection() + + class Handler: + async def handle_DATA(handler, server, session, envelope): + mail = message_from_bytes(envelope.content) + print("got mail", handle_addr(mail["From"]), handle_addr(mail["To"]), mail["Subject"]) + await db.execute_insert("INSERT INTO mails (timestamp, full_mail, from_addr, to_addr, subject) VALUES (?, ?, ?, ?, ?)", + (now().timestamp(), envelope.content, handle_addr(mail["From"]), handle_addr(mail["To"]), decode_mime(mail["Subject"]))) + await db.commit() + return "250 OK" + + controller = UnthreadedController(Handler(), loop=loop, hostname="127.0.0.1") + srv = await controller._create_server() + controller.server = srv + print(controller.hostname, controller.port) + + @routes.get("/") + async def index(req): + page = int(req.query.get("page", 0)) + exclude = [ feed for feed, time in accessed_feeds.items() if (time.timestamp() > (now().timestamp() - 3600)) ] + items = await db.execute_fetchall("SELECT * FROM mails WHERE from_addr NOT IN (SELECT value FROM json_each(?)) ORDER BY timestamp DESC LIMIT 25 OFFSET ?", (json.dumps(exclude), page * 25)) + def display_mail(row): + data = message_from_bytes(row["full_mail"]) + return div([ + div([ datetime.fromtimestamp(row["timestamp"], tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S"), " / ", f"{row['from_addr'] or '[from addr missing]'}→{row['to_addr'] or '[to addr missing]'}", " / ", row["subject"] or "[no subject]" ]), + email_to_html(data, True) + ], cls="entry") + return base_template("Unused Mails", div([ + display_mail(mail) for mail in items + ], cls="mails")) + + @routes.get("/feed/{from}") + async def feed(req): + accessed_feeds[req.match_info["from"]] = now() + items = [] + for mail in await db.execute_fetchall("SELECT * FROM mails WHERE from_addr = ? ORDER BY timestamp DESC LIMIT 20", (req.match_info["from"],)): + data = message_from_bytes(mail["full_mail"]) + content = email_to_html(data, debug_info=False).render() + items.append(rfeed.Item( + title=mail["subject"], + guid=rfeed.Guid(f"smtp2rss-{mail['id']}"), + pubDate=datetime.fromtimestamp(mail["timestamp"], tz=timezone.utc), + author=req.match_info["from"], + description=content.strip() + )) + return web.Response(text=rfeed.Feed( + title=f"{req.match_info['from']} via SMTP2RSS", + lastBuildDate=now(), + link="http://localhost:3394", + description="", + items=items + ).rss()) + + app = web.Application() + app.router.add_routes(routes) + + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, "100.64.0.2", 3394) + await site.start() + +loop = asyncio.get_event_loop_policy().get_event_loop() +loop.run_until_complete(run()) +loop.run_forever() diff --git a/spectro/1200.png b/spectro/1200.png new file mode 100644 index 0000000..cf41b89 Binary files /dev/null and b/spectro/1200.png differ diff --git a/spectro/AColorResources.py b/spectro/AColorResources.py new file mode 100644 index 0000000..194b1a9 --- /dev/null +++ b/spectro/AColorResources.py @@ -0,0 +1,777 @@ +const unsigned char specColormap[256][3] = [ + [ 0, 0, 0], + [ 0, 0, 0], + [ 0, 0, 0], + [ 0, 1, 0], + [ 0, 1, 1], + [ 1, 1, 1], + [ 1, 2, 2], + [ 1, 2, 2], + [ 1, 3, 3], + [ 1, 4, 4], + [ 2, 5, 4], + [ 2, 6, 5], + [ 2, 7, 6], + [ 2, 8, 8], + [ 2, 9, 9], + [ 2, 10, 10], + [ 2, 11, 12], + [ 2, 13, 13], + [ 2, 14, 15], + [ 2, 15, 16], + [ 2, 16, 18], + [ 2, 18, 19], + [ 2, 19, 21], + [ 2, 20, 22], + [ 1, 21, 24], + [ 1, 22, 26], + [ 1, 23, 27], + [ 1, 25, 29], + [ 0, 26, 31], + [ 0, 27, 33], + [ 0, 28, 34], + [ 0, 29, 36], + [ 0, 30, 38], + [ 0, 31, 40], + [ 0, 32, 42], + [ 0, 33, 44], + [ 0, 34, 46], + [ 0, 35, 48], + [ 0, 36, 50], + [ 0, 37, 52], + [ 0, 38, 55], + [ 0, 39, 57], + [ 0, 40, 59], + [ 0, 41, 61], + [ 0, 42, 64], + [ 0, 42, 66], + [ 0, 43, 69], + [ 0, 44, 71], + [ 0, 45, 73], + [ 0, 46, 76], + [ 0, 46, 79], + [ 0, 47, 81], + [ 0, 48, 84], + [ 0, 48, 86], + [ 1, 49, 89], + [ 3, 49, 92], + [ 5, 50, 94], + [ 7, 50, 97], + [ 10, 51, 100], + [ 13, 51, 102], + [ 16, 51, 105], + [ 19, 52, 108], + [ 22, 52, 111], + [ 25, 52, 113], + [ 27, 53, 116], + [ 30, 53, 119], + [ 33, 53, 121], + [ 35, 53, 124], + [ 38, 53, 127], + [ 41, 53, 129], + [ 43, 53, 132], + [ 46, 53, 134], + [ 49, 53, 137], + [ 52, 53, 139], + [ 54, 52, 142], + [ 57, 52, 144], + [ 60, 52, 146], + [ 62, 52, 148], + [ 65, 51, 151], + [ 68, 51, 153], + [ 70, 51, 155], + [ 73, 50, 157], + [ 76, 50, 159], + [ 78, 49, 161], + [ 81, 49, 162], + [ 84, 48, 164], + [ 86, 48, 166], + [ 89, 47, 167], + [ 92, 46, 169], + [ 94, 46, 170], + [ 97, 45, 171], + [ 99, 45, 172], + [102, 44, 173], + [105, 44, 174], + [107, 43, 175], + [110, 42, 176], + [112, 42, 177], + [115, 41, 178], + [117, 41, 178], + [120, 40, 179], + [122, 40, 179], + [124, 39, 179], + [127, 39, 180], + [129, 38, 180], + [132, 38, 180], + [134, 37, 180], + [136, 37, 180], + [139, 37, 179], + [141, 37, 179], + [143, 36, 179], + [146, 36, 178], + [148, 36, 178], + [150, 36, 177], + [152, 36, 176], + [155, 36, 176], + [157, 36, 175], + [159, 36, 174], + [161, 36, 173], + [163, 37, 172], + [165, 37, 171], + [167, 37, 170], + [170, 38, 169], + [172, 38, 167], + [174, 39, 166], + [176, 39, 165], + [178, 40, 163], + [180, 41, 162], + [182, 41, 160], + [183, 42, 159], + [185, 43, 157], + [187, 44, 155], + [189, 45, 154], + [191, 45, 152], + [193, 46, 150], + [195, 47, 149], + [196, 48, 147], + [198, 49, 145], + [200, 50, 143], + [202, 51, 141], + [203, 53, 139], + [205, 54, 138], + [207, 55, 136], + [208, 56, 134], + [210, 57, 132], + [211, 59, 130], + [213, 60, 128], + [214, 61, 126], + [216, 63, 124], + [217, 64, 122], + [219, 65, 120], + [220, 67, 118], + [222, 68, 116], + [223, 70, 114], + [224, 71, 112], + [226, 72, 110], + [227, 74, 108], + [228, 75, 106], + [230, 77, 104], + [231, 78, 102], + [232, 80, 100], + [233, 81, 98], + [234, 83, 96], + [236, 84, 94], + [237, 86, 92], + [238, 87, 91], + [239, 89, 89], + [240, 91, 87], + [241, 92, 85], + [242, 94, 83], + [243, 96, 81], + [244, 97, 79], + [245, 99, 77], + [246, 101, 75], + [247, 102, 73], + [248, 104, 71], + [249, 106, 69], + [249, 108, 67], + [250, 110, 65], + [251, 111, 63], + [251, 113, 61], + [252, 115, 59], + [253, 117, 57], + [253, 119, 55], + [254, 121, 53], + [254, 123, 51], + [254, 125, 49], + [255, 127, 48], + [255, 129, 46], + [255, 131, 44], + [255, 134, 43], + [255, 136, 41], + [255, 138, 40], + [255, 140, 39], + [255, 142, 38], + [255, 145, 37], + [255, 147, 36], + [255, 149, 35], + [255, 151, 35], + [254, 154, 35], + [254, 156, 36], + [253, 158, 36], + [253, 161, 37], + [252, 163, 38], + [252, 166, 40], + [251, 168, 41], + [250, 170, 43], + [249, 173, 45], + [248, 175, 48], + [248, 177, 50], + [247, 180, 53], + [246, 182, 56], + [245, 185, 59], + [244, 187, 63], + [242, 189, 66], + [241, 192, 70], + [240, 194, 73], + [239, 196, 77], + [238, 198, 81], + [237, 200, 85], + [236, 203, 90], + [234, 205, 94], + [233, 207, 99], + [232, 209, 103], + [231, 211, 108], + [230, 213, 112], + [229, 215, 117], + [228, 217, 122], + [227, 219, 127], + [227, 220, 132], + [226, 222, 137], + [226, 224, 142], + [225, 225, 147], + [225, 227, 152], + [225, 228, 157], + [225, 230, 163], + [225, 231, 168], + [225, 233, 173], + [226, 234, 178], + [226, 235, 183], + [227, 236, 188], + [228, 237, 192], + [229, 238, 197], + [230, 239, 202], + [232, 240, 206], + [233, 241, 211], + [235, 242, 215], + [236, 243, 219], + [238, 244, 223], + [240, 245, 227], + [242, 245, 231], + [244, 246, 235], + [247, 247, 238], + [249, 248, 241], + [251, 248, 245], + [253, 249, 247], + [255, 250, 250], +]; + +const unsigned char selColormap[256][3] = [ + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 78, 77], + [ 77, 78, 78], + [ 77, 78, 78], + [ 77, 79, 79], + [ 78, 80, 79], + [ 78, 80, 80], + [ 78, 81, 81], + [ 78, 82, 82], + [ 78, 83, 83], + [ 78, 84, 84], + [ 78, 85, 85], + [ 78, 86, 86], + [ 78, 87, 87], + [ 78, 88, 88], + [ 78, 89, 89], + [ 78, 90, 91], + [ 78, 91, 92], + [ 78, 91, 93], + [ 78, 92, 94], + [ 78, 93, 96], + [ 77, 94, 97], + [ 77, 95, 98], + [ 77, 96, 100], + [ 77, 97, 101], + [ 76, 98, 103], + [ 76, 99, 104], + [ 76, 100, 105], + [ 75, 100, 107], + [ 75, 101, 109], + [ 75, 102, 110], + [ 74, 103, 112], + [ 74, 104, 113], + [ 74, 105, 115], + [ 73, 105, 117], + [ 73, 106, 118], + [ 72, 107, 120], + [ 72, 108, 122], + [ 72, 108, 124], + [ 72, 109, 126], + [ 72, 110, 127], + [ 72, 110, 129], + [ 72, 111, 131], + [ 72, 112, 133], + [ 72, 112, 135], + [ 72, 113, 137], + [ 73, 113, 139], + [ 74, 114, 141], + [ 75, 115, 143], + [ 76, 115, 146], + [ 77, 116, 148], + [ 79, 116, 150], + [ 80, 116, 152], + [ 82, 117, 154], + [ 85, 117, 156], + [ 87, 117, 158], + [ 90, 118, 161], + [ 92, 118, 163], + [ 94, 118, 165], + [ 96, 118, 167], + [ 98, 119, 169], + [101, 119, 171], + [103, 119, 173], + [105, 119, 176], + [107, 119, 178], + [109, 119, 180], + [111, 119, 182], + [113, 119, 184], + [116, 119, 186], + [118, 119, 188], + [120, 118, 190], + [122, 118, 192], + [124, 118, 193], + [126, 118, 195], + [128, 118, 197], + [131, 117, 199], + [133, 117, 200], + [135, 117, 202], + [137, 116, 203], + [139, 116, 205], + [141, 115, 206], + [143, 115, 208], + [146, 115, 209], + [148, 114, 210], + [150, 114, 211], + [152, 113, 212], + [154, 113, 213], + [156, 112, 214], + [158, 112, 215], + [160, 111, 216], + [162, 111, 217], + [164, 110, 217], + [166, 110, 218], + [168, 110, 219], + [170, 109, 219], + [172, 109, 219], + [174, 108, 220], + [176, 108, 220], + [178, 107, 220], + [180, 107, 220], + [182, 107, 220], + [184, 106, 220], + [186, 106, 220], + [187, 106, 220], + [189, 106, 220], + [191, 106, 219], + [193, 105, 219], + [195, 105, 219], + [197, 105, 218], + [198, 105, 218], + [200, 105, 217], + [202, 105, 216], + [204, 105, 216], + [205, 106, 215], + [207, 106, 214], + [209, 106, 213], + [210, 106, 212], + [212, 107, 211], + [214, 107, 210], + [215, 108, 209], + [217, 108, 208], + [219, 108, 207], + [220, 109, 206], + [222, 110, 205], + [223, 110, 203], + [225, 111, 202], + [226, 111, 201], + [228, 112, 200], + [229, 113, 198], + [231, 114, 197], + [232, 114, 195], + [234, 115, 194], + [235, 116, 193], + [236, 117, 191], + [238, 118, 190], + [239, 119, 188], + [240, 120, 187], + [242, 120, 185], + [243, 121, 183], + [244, 122, 182], + [246, 123, 180], + [247, 124, 179], + [248, 126, 177], + [249, 127, 176], + [250, 128, 174], + [252, 129, 173], + [253, 130, 171], + [254, 131, 169], + [255, 132, 168], + [255, 133, 166], + [255, 134, 165], + [255, 136, 163], + [255, 137, 162], + [255, 138, 160], + [255, 139, 158], + [255, 140, 157], + [255, 142, 155], + [255, 143, 154], + [255, 144, 152], + [255, 145, 150], + [255, 146, 149], + [255, 148, 147], + [255, 149, 146], + [255, 150, 144], + [255, 152, 143], + [255, 153, 141], + [255, 154, 139], + [255, 156, 138], + [255, 157, 136], + [255, 158, 135], + [255, 160, 133], + [255, 161, 131], + [255, 163, 130], + [255, 164, 128], + [255, 166, 127], + [255, 167, 125], + [255, 169, 124], + [255, 170, 122], + [255, 172, 120], + [255, 173, 119], + [255, 175, 117], + [255, 177, 116], + [255, 178, 115], + [255, 180, 113], + [255, 182, 112], + [255, 183, 111], + [255, 185, 109], + [255, 187, 108], + [255, 189, 107], + [255, 190, 107], + [255, 192, 106], + [255, 194, 105], + [255, 196, 105], + [255, 198, 105], + [255, 200, 105], + [255, 201, 105], + [255, 203, 105], + [255, 205, 106], + [255, 207, 107], + [255, 209, 108], + [255, 211, 109], + [255, 213, 111], + [255, 215, 113], + [255, 217, 115], + [255, 218, 117], + [255, 220, 119], + [255, 222, 121], + [255, 224, 124], + [255, 226, 127], + [255, 228, 129], + [255, 230, 132], + [255, 232, 135], + [255, 233, 138], + [255, 235, 142], + [255, 237, 145], + [255, 239, 148], + [255, 240, 152], + [255, 242, 155], + [255, 244, 159], + [255, 245, 163], + [255, 247, 166], + [255, 248, 170], + [255, 250, 174], + [255, 251, 178], + [255, 253, 182], + [255, 254, 186], + [255, 255, 190], + [255, 255, 194], + [255, 255, 198], + [255, 255, 202], + [255, 255, 207], + [255, 255, 211], + [255, 255, 215], + [255, 255, 219], + [255, 255, 223], + [255, 255, 227], + [255, 255, 230], + [255, 255, 234], + [255, 255, 238], + [255, 255, 242], + [255, 255, 245], + [255, 255, 249], + [255, 255, 252], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], +]; + +const unsigned char freqSelColormap[256][3] = [ + [ 62, 65, 89], + [ 63, 65, 91], + [ 64, 66, 92], + [ 65, 66, 94], + [ 66, 66, 95], + [ 67, 67, 97], + [ 68, 67, 98], + [ 70, 67, 100], + [ 71, 68, 101], + [ 72, 68, 103], + [ 74, 68, 104], + [ 75, 69, 106], + [ 76, 69, 107], + [ 78, 69, 109], + [ 79, 69, 110], + [ 81, 69, 112], + [ 82, 70, 113], + [ 84, 70, 115], + [ 86, 70, 116], + [ 87, 70, 118], + [ 89, 70, 119], + [ 91, 70, 120], + [ 92, 70, 122], + [ 94, 70, 123], + [ 96, 70, 124], + [ 98, 70, 126], + [100, 70, 127], + [102, 70, 128], + [104, 70, 129], + [106, 69, 130], + [108, 69, 132], + [110, 69, 133], + [112, 69, 134], + [114, 69, 135], + [116, 68, 136], + [118, 68, 137], + [120, 68, 138], + [122, 67, 138], + [124, 67, 139], + [126, 66, 140], + [129, 66, 141], + [131, 65, 141], + [133, 65, 142], + [135, 64, 142], + [138, 64, 142], + [140, 63, 143], + [142, 63, 143], + [144, 62, 143], + [147, 61, 143], + [149, 61, 143], + [151, 60, 143], + [153, 59, 143], + [156, 59, 142], + [158, 58, 142], + [160, 57, 141], + [162, 57, 141], + [165, 56, 140], + [167, 55, 139], + [169, 55, 138], + [171, 54, 137], + [173, 53, 136], + [176, 53, 135], + [178, 52, 134], + [180, 51, 133], + [182, 51, 131], + [184, 50, 130], + [186, 50, 128], + [188, 49, 127], + [190, 49, 125], + [192, 49, 123], + [194, 48, 121], + [196, 48, 119], + [198, 48, 117], + [200, 48, 115], + [202, 48, 113], + [203, 47, 110], + [205, 47, 108], + [207, 48, 106], + [209, 48, 103], + [210, 48, 101], + [212, 48, 98], + [214, 48, 96], + [215, 49, 93], + [217, 49, 91], + [218, 50, 88], + [220, 50, 85], + [221, 51, 83], + [223, 52, 80], + [224, 53, 77], + [225, 54, 74], + [226, 55, 71], + [228, 56, 68], + [229, 57, 65], + [230, 58, 62], + [231, 59, 59], + [232, 60, 56], + [233, 62, 53], + [234, 63, 49], + [235, 64, 46], + [236, 66, 42], + [237, 67, 38], + [238, 69, 35], + [239, 71, 30], + [239, 72, 26], + [240, 74, 20], + [241, 76, 14], + [241, 77, 5], + [242, 79, 0], + [243, 81, 0], + [243, 83, 0], + [244, 85, 0], + [244, 87, 0], + [244, 89, 0], + [245, 91, 0], + [245, 93, 0], + [245, 95, 0], + [245, 97, 0], + [245, 99, 0], + [245, 101, 0], + [245, 103, 0], + [245, 105, 0], + [245, 108, 0], + [245, 110, 0], + [245, 112, 0], + [245, 114, 0], + [244, 116, 0], + [244, 119, 0], + [243, 121, 0], + [243, 123, 0], + [242, 126, 0], + [242, 128, 0], + [241, 130, 0], + [241, 132, 0], + [240, 135, 0], + [239, 137, 0], + [238, 139, 0], + [237, 142, 0], + [236, 144, 0], + [235, 146, 0], + [234, 149, 0], + [233, 151, 0], + [232, 154, 0], + [230, 156, 0], + [229, 158, 0], + [227, 161, 0], + [226, 163, 0], + [224, 165, 0], + [223, 168, 0], + [221, 170, 0], + [219, 173, 0], + [217, 175, 0], + [215, 177, 0], + [213, 180, 0], + [211, 182, 0], + [209, 184, 0], + [207, 187, 0], + [205, 189, 0], + [202, 191, 0], + [200, 193, 0], + [197, 196, 0], + [195, 198, 0], + [192, 200, 0], + [189, 203, 0], + [186, 205, 0], + [183, 207, 0], + [180, 209, 0], + [177, 211, 0], + [174, 214, 0], + [170, 216, 0], + [167, 218, 0], + [163, 220, 0], + [159, 222, 0], + [155, 225, 0], + [151, 227, 0], + [147, 229, 0], + [142, 231, 0], + [137, 233, 10], + [132, 235, 24], + [127, 237, 34], + [121, 239, 42], + [116, 241, 50], + [109, 243, 56], + [103, 245, 63], + [ 95, 247, 69], + [ 88, 249, 75], + [ 79, 251, 80], + [ 69, 253, 86], + [ 57, 255, 91], + [ 42, 255, 97], + [ 18, 255, 102], + [ 0, 255, 107], + [ 0, 255, 113], + [ 0, 255, 118], + [ 0, 255, 123], + [ 0, 255, 129], + [ 0, 255, 134], + [ 0, 255, 139], + [ 0, 255, 144], + [ 0, 255, 149], + [ 0, 255, 155], + [ 0, 255, 160], + [ 0, 255, 165], + [ 0, 255, 170], + [ 0, 255, 175], + [ 0, 255, 180], + [ 0, 255, 185], + [ 0, 255, 190], + [ 0, 255, 195], + [ 0, 255, 200], + [ 0, 255, 205], + [ 0, 255, 210], + [ 0, 255, 214], + [ 0, 255, 219], + [ 0, 255, 223], + [ 0, 255, 228], + [ 0, 255, 232], + [ 0, 255, 237], + [ 0, 255, 241], + [ 0, 255, 245], + [ 0, 255, 249], + [ 0, 255, 253], + [ 0, 255, 255], + [ 0, 255, 255], + [ 21, 255, 255], + [ 50, 255, 255], + [ 68, 255, 255], + [ 82, 255, 255], + [ 95, 255, 255], + [106, 255, 255], + [116, 255, 255], + [126, 255, 255], + [135, 255, 255], + [143, 255, 255], + [151, 255, 255], + [159, 255, 255], + [166, 255, 255], + [173, 255, 255], + [180, 255, 255], + [187, 255, 255], + [193, 255, 255], + [199, 255, 255], + [205, 255, 255], + [211, 255, 255], + [216, 255, 255], + [222, 255, 255], + [227, 255, 255], + [232, 255, 255], + [237, 255, 255], + [241, 255, 255], + [246, 255, 255], + [250, 255, 255], + [254, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 253], +]; + diff --git a/spectro/run.py b/spectro/run.py new file mode 100644 index 0000000..24c848f --- /dev/null +++ b/spectro/run.py @@ -0,0 +1,303 @@ +colorscheme = [ + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 77, 77], + [ 77, 78, 77], + [ 77, 78, 78], + [ 77, 78, 78], + [ 77, 79, 79], + [ 78, 80, 79], + [ 78, 80, 80], + [ 78, 81, 81], + [ 78, 82, 82], + [ 78, 83, 83], + [ 78, 84, 84], + [ 78, 85, 85], + [ 78, 86, 86], + [ 78, 87, 87], + [ 78, 88, 88], + [ 78, 89, 89], + [ 78, 90, 91], + [ 78, 91, 92], + [ 78, 91, 93], + [ 78, 92, 94], + [ 78, 93, 96], + [ 77, 94, 97], + [ 77, 95, 98], + [ 77, 96, 100], + [ 77, 97, 101], + [ 76, 98, 103], + [ 76, 99, 104], + [ 76, 100, 105], + [ 75, 100, 107], + [ 75, 101, 109], + [ 75, 102, 110], + [ 74, 103, 112], + [ 74, 104, 113], + [ 74, 105, 115], + [ 73, 105, 117], + [ 73, 106, 118], + [ 72, 107, 120], + [ 72, 108, 122], + [ 72, 108, 124], + [ 72, 109, 126], + [ 72, 110, 127], + [ 72, 110, 129], + [ 72, 111, 131], + [ 72, 112, 133], + [ 72, 112, 135], + [ 72, 113, 137], + [ 73, 113, 139], + [ 74, 114, 141], + [ 75, 115, 143], + [ 76, 115, 146], + [ 77, 116, 148], + [ 79, 116, 150], + [ 80, 116, 152], + [ 82, 117, 154], + [ 85, 117, 156], + [ 87, 117, 158], + [ 90, 118, 161], + [ 92, 118, 163], + [ 94, 118, 165], + [ 96, 118, 167], + [ 98, 119, 169], + [101, 119, 171], + [103, 119, 173], + [105, 119, 176], + [107, 119, 178], + [109, 119, 180], + [111, 119, 182], + [113, 119, 184], + [116, 119, 186], + [118, 119, 188], + [120, 118, 190], + [122, 118, 192], + [124, 118, 193], + [126, 118, 195], + [128, 118, 197], + [131, 117, 199], + [133, 117, 200], + [135, 117, 202], + [137, 116, 203], + [139, 116, 205], + [141, 115, 206], + [143, 115, 208], + [146, 115, 209], + [148, 114, 210], + [150, 114, 211], + [152, 113, 212], + [154, 113, 213], + [156, 112, 214], + [158, 112, 215], + [160, 111, 216], + [162, 111, 217], + [164, 110, 217], + [166, 110, 218], + [168, 110, 219], + [170, 109, 219], + [172, 109, 219], + [174, 108, 220], + [176, 108, 220], + [178, 107, 220], + [180, 107, 220], + [182, 107, 220], + [184, 106, 220], + [186, 106, 220], + [187, 106, 220], + [189, 106, 220], + [191, 106, 219], + [193, 105, 219], + [195, 105, 219], + [197, 105, 218], + [198, 105, 218], + [200, 105, 217], + [202, 105, 216], + [204, 105, 216], + [205, 106, 215], + [207, 106, 214], + [209, 106, 213], + [210, 106, 212], + [212, 107, 211], + [214, 107, 210], + [215, 108, 209], + [217, 108, 208], + [219, 108, 207], + [220, 109, 206], + [222, 110, 205], + [223, 110, 203], + [225, 111, 202], + [226, 111, 201], + [228, 112, 200], + [229, 113, 198], + [231, 114, 197], + [232, 114, 195], + [234, 115, 194], + [235, 116, 193], + [236, 117, 191], + [238, 118, 190], + [239, 119, 188], + [240, 120, 187], + [242, 120, 185], + [243, 121, 183], + [244, 122, 182], + [246, 123, 180], + [247, 124, 179], + [248, 126, 177], + [249, 127, 176], + [250, 128, 174], + [252, 129, 173], + [253, 130, 171], + [254, 131, 169], + [255, 132, 168], + [255, 133, 166], + [255, 134, 165], + [255, 136, 163], + [255, 137, 162], + [255, 138, 160], + [255, 139, 158], + [255, 140, 157], + [255, 142, 155], + [255, 143, 154], + [255, 144, 152], + [255, 145, 150], + [255, 146, 149], + [255, 148, 147], + [255, 149, 146], + [255, 150, 144], + [255, 152, 143], + [255, 153, 141], + [255, 154, 139], + [255, 156, 138], + [255, 157, 136], + [255, 158, 135], + [255, 160, 133], + [255, 161, 131], + [255, 163, 130], + [255, 164, 128], + [255, 166, 127], + [255, 167, 125], + [255, 169, 124], + [255, 170, 122], + [255, 172, 120], + [255, 173, 119], + [255, 175, 117], + [255, 177, 116], + [255, 178, 115], + [255, 180, 113], + [255, 182, 112], + [255, 183, 111], + [255, 185, 109], + [255, 187, 108], + [255, 189, 107], + [255, 190, 107], + [255, 192, 106], + [255, 194, 105], + [255, 196, 105], + [255, 198, 105], + [255, 200, 105], + [255, 201, 105], + [255, 203, 105], + [255, 205, 106], + [255, 207, 107], + [255, 209, 108], + [255, 211, 109], + [255, 213, 111], + [255, 215, 113], + [255, 217, 115], + [255, 218, 117], + [255, 220, 119], + [255, 222, 121], + [255, 224, 124], + [255, 226, 127], + [255, 228, 129], + [255, 230, 132], + [255, 232, 135], + [255, 233, 138], + [255, 235, 142], + [255, 237, 145], + [255, 239, 148], + [255, 240, 152], + [255, 242, 155], + [255, 244, 159], + [255, 245, 163], + [255, 247, 166], + [255, 248, 170], + [255, 250, 174], + [255, 251, 178], + [255, 253, 182], + [255, 254, 186], + [255, 255, 190], + [255, 255, 194], + [255, 255, 198], + [255, 255, 202], + [255, 255, 207], + [255, 255, 211], + [255, 255, 215], + [255, 255, 219], + [255, 255, 223], + [255, 255, 227], + [255, 255, 230], + [255, 255, 234], + [255, 255, 238], + [255, 255, 242], + [255, 255, 245], + [255, 255, 249], + [255, 255, 252], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], + [255, 255, 255], +]; + +from PIL import Image +import numpy as np +import wave, math +import os, pickle + +cols = np.array(colorscheme) + +def nearest_color(x): + return np.argmin(np.linalg.norm(x - cols, axis=1)) + +im = np.array(Image.open("1200.png"))[:-1, ..., :3] +if not os.path.exists("save.pkl"): + spectro = np.apply_along_axis(nearest_color, -1, im) + pickle.dump(spectro, open("save.pkl", "wb")) +else: + spectro = pickle.load(open("save.pkl", "rb")) +print(spectro) +samples = 48000 +actual_samples = samples * 4 # 4 seconds of audio time +print(im.shape) +spectro = spectro.transpose() # axis 0 is horizontal in image and axis 1 is vertical, probably +spectro = spectro.astype(np.float) +spectro -= spectro.min() +out = np.zeros(actual_samples) +ω = np.linspace(0, 10_000, num=im.shape[0]) * 2 * math.pi +for i in range(actual_samples): + index = i / actual_samples * (im.shape[1] - 1) + p, n = math.floor(index), math.ceil(index) + d = index - p + spec = spectro[p] * (1-d) + spectro[n] * d + θ = ω * (i / samples) + a = np.sin(θ * spec) + out[i] = sum(a) + if i % 1000 == 0: print(i) +print(out) +out /= max(out) +out *= 16384 +print(out) +data = out.astype(" + \ No newline at end of file