pdfunite $(find . -maxdepth 1 -type f -name '*.pdf' ! -name 'merged.pdf' | sort) merged.pdf
Thursday, October 1, 2026
merging pdfs sorted alphabetically
Wednesday, September 16, 2026
Friday, September 11, 2026
Best Free Fx Plugins 2026
put down production for a while. digging through the pile these gems from tiagolr, baconpaul, and blepfx stand out.
Tiagolr
time 12gate12 filtr
Other Tiagolr Plugins I haven't tried yet that look interesting
quadmorph filter qdelay tetra op - 4op fm wavetable synthBlepfx
filtrr - the best filterBaconpaul
two-filters
#the jsfx version of tiagolr for reaper
https://raw.githubusercontent.com/tiagolr/tilr_jsfx/master/index.xml
Wednesday, September 9, 2026
gba gsm audio player improved
this version allows album art.
I think it also allows you to drop in wavs? the other music player required sox conversation and I always forget the command maybe this will be easier.
m8 tracker is ios native
40$ cad
been thinking about picking up
amber opl instead but its ipad only and I already have deflemask.
Friday, September 4, 2026
Midi Program Changer
Made this in python to change programs with the plus and minus keys on a qwerty keyboard. The easiest way to use is run the linux x86 build on github that I packaged together with pyinstall so you dont need to manually deal with dependencies. Otherwise if your own win/mac heres the python.
#!/usr/bin/env python3
import tkinter as tk
from tkinter import ttk, messagebox
import mido
from pynput import keyboard
# ============================================================
# GENERAL MIDI INSTRUMENTS
# ============================================================
GM_INSTRUMENTS = [
"Acoustic Grand Piano",
"Bright Acoustic Piano",
"Electric Grand Piano",
"Honky-tonk Piano",
"Electric Piano 1",
"Electric Piano 2",
"Harpsichord",
"Clavinet",
"Celesta",
"Glockenspiel",
"Music Box",
"Vibraphone",
"Marimba",
"Xylophone",
"Tubular Bells",
"Dulcimer",
"Drawbar Organ",
"Percussive Organ",
"Rock Organ",
"Church Organ",
"Reed Organ",
"Accordion",
"Harmonica",
"Tango Accordion",
"Acoustic Guitar (nylon)",
"Acoustic Guitar (steel)",
"Electric Guitar (jazz)",
"Electric Guitar (clean)",
"Electric Guitar (muted)",
"Overdriven Guitar",
"Distortion Guitar",
"Guitar Harmonics",
"Acoustic Bass",
"Electric Bass (finger)",
"Electric Bass (pick)",
"Fretless Bass",
"Slap Bass 1",
"Slap Bass 2",
"Synth Bass 1",
"Synth Bass 2",
"Violin",
"Viola",
"Cello",
"Contrabass",
"Tremolo Strings",
"Pizzicato Strings",
"Orchestral Harp",
"Timpani",
"String Ensemble 1",
"String Ensemble 2",
"SynthStrings 1",
"SynthStrings 2",
"Choir Aahs",
"Voice Oohs",
"Synth Voice",
"Orchestra Hit",
"Trumpet",
"Trombone",
"Tuba",
"Muted Trumpet",
"French Horn",
"Brass Section",
"SynthBrass 1",
"SynthBrass 2",
"Soprano Sax",
"Alto Sax",
"Tenor Sax",
"Baritone Sax",
"Oboe",
"English Horn",
"Bassoon",
"Clarinet",
"Piccolo",
"Flute",
"Recorder",
"Pan Flute",
"Blown Bottle",
"Shakuhachi",
"Whistle",
"Ocarina",
"Lead 1 (square)",
"Lead 2 (sawtooth)",
"Lead 3 (calliope)",
"Lead 4 (chiff)",
"Lead 5 (charang)",
"Lead 6 (voice)",
"Lead 7 (fifths)",
"Lead 8 (bass + lead)",
"Pad 1 (new age)",
"Pad 2 (warm)",
"Pad 3 (polysynth)",
"Pad 4 (choir)",
"Pad 5 (bowed)",
"Pad 6 (metallic)",
"Pad 7 (halo)",
"Pad 8 (sweep)",
"FX 1 (rain)",
"FX 2 (soundtrack)",
"FX 3 (crystal)",
"FX 4 (atmosphere)",
"FX 5 (brightness)",
"FX 6 (goblins)",
"FX 7 (echoes)",
"FX 8 (sci-fi)",
"Sitar",
"Banjo",
"Shamisen",
"Koto",
"Kalimba",
"Bagpipe",
"Fiddle",
"Shanai",
"Tinkle Bell",
"Agogo",
"Steel Drums",
"Woodblock",
"Taiko Drum",
"Melodic Tom",
"Synth Drum",
"Reverse Cymbal",
"Guitar Fret Noise",
"Breath Noise",
"Seashore",
"Bird Tweet",
"Telephone Ring",
"Helicopter",
"Applause",
"Gunshot",
]
# ============================================================
# CHANNEL 10 DRUM KITS
# ============================================================
DRUM_KITS = {
1: "Standard Drum Kit",
9: "Room Drum Kit",
17: "Power Drum Kit",
25: "Electric Drum Kit",
26: "Rap TR808 Drums",
33: "Jazz Drum Kit",
41: "Brush Kit",
}
DRUM_PROGRAMS = list(DRUM_KITS.keys())
# ============================================================
# MIDI CC
# ============================================================
REVERB_CC = 91
# ============================================================
# APPLICATION
# ============================================================
class MidiProgramChanger:
def __init__(self, root):
self.root = root
self.root.title("MIDI Program Changer")
self.port = None
self.keyboard_listener = None
self.program = 1
self.channel = 1
self.reverb = 0
self.matrix_var = tk.IntVar(value=self.program)
self.reverb_var = tk.IntVar(value=self.reverb)
self.matrix_buttons = []
self.create_gui()
self.refresh_ports()
self.start_keyboard_listener()
self.root.protocol(
"WM_DELETE_WINDOW",
self.close
)
# ========================================================
# GUI
# ========================================================
def create_gui(self):
main = ttk.Frame(
self.root,
padding=10
)
main.pack(
fill="both",
expand=True
)
# ----------------------------------------------------
# MIDI OUTPUT
# ----------------------------------------------------
port_frame = ttk.LabelFrame(
main,
text="MIDI Output",
padding=8
)
port_frame.pack(
fill="x",
pady=(0, 8)
)
self.port_var = tk.StringVar()
self.port_combo = ttk.Combobox(
port_frame,
textvariable=self.port_var,
state="readonly",
width=55
)
self.port_combo.pack(
side="left",
fill="x",
expand=True
)
ttk.Button(
port_frame,
text="Refresh",
command=self.refresh_ports
).pack(
side="left",
padx=(5, 0)
)
ttk.Button(
port_frame,
text="Connect",
command=self.connect_port
).pack(
side="left",
padx=(5, 0)
)
# ----------------------------------------------------
# CHANNEL
# ----------------------------------------------------
channel_frame = ttk.Frame(main)
channel_frame.pack(
fill="x",
pady=(0, 8)
)
ttk.Label(
channel_frame,
text="MIDI Channel:"
).pack(side="left")
self.channel_var = tk.IntVar(value=1)
self.channel_spin = tk.Spinbox(
channel_frame,
from_=1,
to=16,
textvariable=self.channel_var,
width=5,
command=self.channel_changed
)
self.channel_spin.pack(
side="left",
padx=(5, 0)
)
self.channel_spin.bind(
"",
lambda event: self.channel_changed()
)
self.channel_spin.bind(
"",
lambda event: self.channel_changed()
)
# ----------------------------------------------------
# PROGRAM CONTROLS
# ----------------------------------------------------
control_frame = ttk.Frame(main)
control_frame.pack(
fill="x",
pady=(0, 8)
)
ttk.Button(
control_frame,
text="Previous",
command=self.previous_program
).pack(side="left")
self.program_var = tk.IntVar(value=1)
self.program_spin = tk.Spinbox(
control_frame,
from_=1,
to=128,
textvariable=self.program_var,
width=6,
command=self.program_spin_changed
)
self.program_spin.pack(
side="left",
padx=8
)
self.program_spin.bind(
"",
lambda event: self.program_spin_changed()
)
self.program_spin.bind(
"",
lambda event: self.program_spin_changed()
)
ttk.Button(
control_frame,
text="Next",
command=self.next_program
).pack(side="left")
# ----------------------------------------------------
# INSTRUMENT NAME
# ----------------------------------------------------
self.instrument_label = ttk.Label(
main,
text="1 Acoustic Grand Piano",
anchor="center",
font=("TkDefaultFont", 12, "bold")
)
self.instrument_label.pack(
fill="x",
pady=(0, 8)
)
# ----------------------------------------------------
# REVERB
# ----------------------------------------------------
reverb_frame = ttk.LabelFrame(
main,
text="Reverb (MIDI CC 91)",
padding=8
)
reverb_frame.pack(
fill="x",
pady=(0, 8)
)
self.reverb_knob = tk.Scale(
reverb_frame,
from_=0,
to=127,
orient="horizontal",
variable=self.reverb_var,
command=self.reverb_changed,
showvalue=True,
resolution=1,
length=300
)
self.reverb_knob.pack(
fill="x"
)
# ----------------------------------------------------
# SCROLLABLE PROGRAM MATRIX
# ----------------------------------------------------
matrix_frame = ttk.LabelFrame(
main,
text="Program",
padding=5
)
matrix_frame.pack(
fill="both",
expand=True
)
# Canvas
self.canvas = tk.Canvas(
matrix_frame,
highlightthickness=0
)
self.canvas.pack(
side="left",
fill="both",
expand=True
)
# Vertical scrollbar
scrollbar = ttk.Scrollbar(
matrix_frame,
orient="vertical",
command=self.canvas.yview
)
scrollbar.pack(
side="right",
fill="y"
)
self.canvas.configure(
yscrollcommand=scrollbar.set
)
# Frame inside canvas
self.matrix_inner = ttk.Frame(
self.canvas
)
self.canvas_window = self.canvas.create_window(
(0, 0),
window=self.matrix_inner,
anchor="nw"
)
# Update scroll region when frame changes
self.matrix_inner.bind(
"",
self.update_scroll_region
)
# Make inner frame follow canvas width
self.canvas.bind(
"",
self.resize_matrix
)
# Mouse wheel
self.canvas.bind_all(
"",
self.mousewheel
)
# Linux mouse wheel
self.canvas.bind_all(
"",
self.mousewheel_linux_up
)
self.canvas.bind_all(
"",
self.mousewheel_linux_down
)
# ----------------------------------------------------
# CREATE MATRIX
# ----------------------------------------------------
for program in range(1, 129):
row = (program - 1) // 8
col = (program - 1) % 8
button = ttk.Radiobutton(
self.matrix_inner,
text=str(program),
value=program,
variable=self.matrix_var,
command=lambda p=program:
self.matrix_program_selected(p)
)
button.grid(
row=row,
column=col,
padx=2,
pady=2,
sticky="ew"
)
self.matrix_buttons.append(button)
for col in range(8):
self.matrix_inner.columnconfigure(
col,
weight=1
)
self.update_program_controls()
self.update_matrix()
self.update_label()
# ========================================================
# SCROLLING
# ========================================================
def update_scroll_region(self, event=None):
self.canvas.configure(
scrollregion=self.canvas.bbox("all")
)
def resize_matrix(self, event):
self.canvas.itemconfigure(
self.canvas_window,
width=event.width
)
def mousewheel(self, event):
self.canvas.yview_scroll(
int(-1 * (event.delta / 120)),
"units"
)
def mousewheel_linux_up(self, event):
self.canvas.yview_scroll(
-3,
"units"
)
def mousewheel_linux_down(self, event):
self.canvas.yview_scroll(
3,
"units"
)
# ========================================================
# MIDI PORTS
# ========================================================
def refresh_ports(self):
ports = mido.get_output_names()
self.port_combo["values"] = ports
if ports:
self.port_combo.current(0)
else:
self.port_var.set("")
def connect_port(self):
name = self.port_var.get()
if not name:
messagebox.showwarning(
"MIDI",
"No MIDI output port selected."
)
return
try:
if self.port:
self.port.close()
self.port = mido.open_output(name)
self.send_program()
self.send_reverb()
except Exception as e:
self.port = None
messagebox.showerror(
"MIDI Error",
str(e)
)
# ========================================================
# MIDI PROGRAM CHANGE
# ========================================================
def send_program(self):
if self.port is None:
return
midi_channel = self.channel - 1
midi_program = self.program - 1
try:
self.port.send(
mido.Message(
"program_change",
channel=midi_channel,
program=midi_program
)
)
except Exception as e:
print("MIDI error:", e)
# ========================================================
# REVERB CC 91
# ========================================================
def send_reverb(self):
if self.port is None:
return
midi_channel = self.channel - 1
try:
self.port.send(
mido.Message(
"control_change",
channel=midi_channel,
control=REVERB_CC,
value=self.reverb
)
)
except Exception as e:
print("MIDI reverb error:", e)
def reverb_changed(self, value):
try:
self.reverb = int(float(value))
except ValueError:
return
self.send_reverb()
# ========================================================
# CHANNEL
# ========================================================
def channel_changed(self):
try:
channel = int(
self.channel_var.get()
)
if channel < 1 or channel > 16:
raise ValueError
except ValueError:
self.channel_var.set(
self.channel
)
return
self.channel = channel
# Channel 10 only supports the selected drum kits.
if self.channel == 10:
if self.program not in DRUM_KITS:
self.program = DRUM_PROGRAMS[0]
self.program_var.set(
self.program
)
self.matrix_var.set(
self.program
)
self.update_program_controls()
self.update_matrix()
self.update_label()
self.send_program()
self.send_reverb()
# ========================================================
# PROGRAM
# ========================================================
def set_program(self, program):
if self.channel == 10:
if program not in DRUM_KITS:
return
if program < 1 or program > 128:
return
self.program = program
self.program_var.set(
program
)
self.matrix_var.set(
program
)
self.update_label()
self.update_matrix()
self.send_program()
def program_spin_changed(self):
try:
program = int(
self.program_var.get()
)
except ValueError:
self.program_var.set(
self.program
)
return
if self.channel == 10:
if program not in DRUM_KITS:
program = min(
DRUM_PROGRAMS,
key=lambda p:
abs(p - program)
)
else:
if program < 1:
program = 1
if program > 128:
program = 128
self.set_program(program)
# ========================================================
# PREVIOUS
# ========================================================
def previous_program(self):
if self.channel == 10:
current_index = DRUM_PROGRAMS.index(
self.program
)
new_index = (
current_index - 1
) % len(DRUM_PROGRAMS)
self.set_program(
DRUM_PROGRAMS[new_index]
)
else:
if self.program <= 1:
new_program = 128
else:
new_program = self.program - 1
self.set_program(
new_program
)
# ========================================================
# NEXT
# ========================================================
def next_program(self):
if self.channel == 10:
current_index = DRUM_PROGRAMS.index(
self.program
)
new_index = (
current_index + 1
) % len(DRUM_PROGRAMS)
self.set_program(
DRUM_PROGRAMS[new_index]
)
else:
if self.program >= 128:
new_program = 1
else:
new_program = self.program + 1
self.set_program(
new_program
)
# ========================================================
# MATRIX
# ========================================================
def matrix_program_selected(self, program):
self.set_program(program)
def update_matrix(self):
if not self.matrix_buttons:
return
for program, button in enumerate(
self.matrix_buttons,
start=1
):
if self.channel == 10:
if program in DRUM_KITS:
button.configure(
text=f"{program}\n"
f"{DRUM_KITS[program]}",
state="normal"
)
else:
button.configure(
text=str(program),
state="disabled"
)
else:
button.configure(
text=str(program),
state="normal"
)
# ========================================================
# PROGRAM SPINBOX
# ========================================================
def update_program_controls(self):
if self.channel == 10:
self.program_spin.configure(
values=tuple(
DRUM_PROGRAMS
)
)
else:
self.program_spin.configure(
from_=1,
to=128,
values=()
)
# ========================================================
# LABEL
# ========================================================
def update_label(self):
if self.channel == 10:
name = DRUM_KITS.get(
self.program,
"Unknown Drum Kit"
)
else:
name = GM_INSTRUMENTS[
self.program - 1
]
self.instrument_label.configure(
text=f"{self.program} {name}"
)
# ========================================================
# GLOBAL KEYBOARD
# ========================================================
def start_keyboard_listener(self):
self.keyboard_listener = keyboard.Listener(
on_press=self.key_pressed
)
self.keyboard_listener.start()
def key_pressed(self, key):
try:
if key.char == "-":
self.root.after(
0,
self.previous_program
)
elif key.char == "=":
self.root.after(
0,
self.next_program
)
except AttributeError:
pass
# ========================================================
# CLOSE
# ========================================================
def close(self):
if self.keyboard_listener:
self.keyboard_listener.stop()
if self.port:
self.port.close()
self.root.destroy()
# ============================================================
# MAIN
# ============================================================
if __name__ == "__main__":
root = tk.Tk()
app = MidiProgramChanger(root)
root.mainloop()
Sunday, August 30, 2026
Saturday, August 29, 2026
fixing vgm2gbs for linux
once you make a gbs you can convert it to a gb rom using (or just do gb export in deflemask and skip all this)
gbs-master vgm2gbs githubconvert.bat is great if your on windows for drag and drop but 2 problems
1 - vgm headers this script used are old and incompatable with most newer deflemask versions
2 - because im running it in wine and requires python its broken
THE FIX
Replace src/gd3.py with:
make a linux.sh and chmod +x it
#!/bin/bash
cd "$(dirname "$0")"
python3 src/main.py "$1"
import struct
class GD3:
version: int
track_name: str
track_name_orig: str
game_name: str
game_name_orig: str
system_name: str
system_name_orig: str
author: str
author_orig: str
release_date: str
ripper: str
notes: str
def __init__(self, data: bytes):
assert data[0:4] == b'Gd3 '
self.version, data_length = struct.unpack_from(" str:
return f'{self.track_name} - {self.author} - {self.game_name} ({self.ripper})'
making lsdj roms with lsdpack kit
make RGBDS="$HOME/Downloads/rgbds-0.9.1-linux-x86_64/"
rgbds I needed the older version to compile
lsdj rom repo
I use v 8-5-1 stable the newer versions output a garbled mess
in the code_config.mk file i commented out
# ASFLAGS += -h
this projects cool because it lets you plop in your 160x144 album pics with your lsdj songs to make the rom for an interactive album
Friday, August 28, 2026
logitech f310 macros, shutdown, copy paste
from evdev import InputDevice, UInput, ecodes
import subprocess
import threading
import time
GAMEPAD = "/dev/input/by-id/usb-Logitech_Logitech_Dual_Action_DF5CB332-event-joystick"
device = InputDevice(GAMEPAD)
# Virtual keyboard
ui = UInput({
ecodes.EV_KEY: [
ecodes.KEY_LEFTCTRL,
ecodes.KEY_C,
ecodes.KEY_V,
]
}, name="Gamepad Virtual Keyboard")
button_292 = False
button_293 = False
shutdown_timer = None
lock = threading.Lock()
def ctrl_key(key):
print(f"Sending Ctrl+{key}", flush=True)
ui.write(ecodes.EV_KEY, ecodes.KEY_LEFTCTRL, 1)
ui.write(ecodes.EV_KEY, key, 1)
ui.write(ecodes.EV_KEY, key, 0)
ui.write(ecodes.EV_KEY, ecodes.KEY_LEFTCTRL, 0)
ui.syn()
def shutdown():
time.sleep(1)
with lock:
if button_292 and button_293:
print("\nBoth buttons held for 1 second — SHUTTING DOWN!", flush=True)
subprocess.run(["systemctl", "poweroff"])
print(f"Gamepad: {device.name}", flush=True)
print("291 = Ctrl+C", flush=True)
print("290 = Ctrl+V", flush=True)
print("292 + 293 for 1 second = Shutdown", flush=True)
print("Waiting for input...\n", flush=True)
for event in device.read_loop():
if event.type != ecodes.EV_KEY:
continue
with lock:
# BTN_TOP = 291 -> Ctrl+C
if event.code == 291 and event.value == 1:
print("291: Ctrl+C", flush=True)
ctrl_key(ecodes.KEY_C)
# BTN_THUMB2 = 290 -> Ctrl+V
elif event.code == 290 and event.value == 1:
print("290: Ctrl+V", flush=True)
ctrl_key(ecodes.KEY_V)
# BTN_TOP2 = 292
elif event.code == 292:
button_292 = event.value == 1
print(
f"292: {'PRESSED' if button_292 else 'RELEASED'}",
flush=True
)
# BTN_PINKIE = 293
elif event.code == 293:
button_293 = event.value == 1
print(
f"293: {'PRESSED' if button_293 else 'RELEASED'}",
flush=True
)
# Start shutdown timer
if button_292 and button_293 and shutdown_timer is None:
print("Both shoulder buttons held — 1 second timer started.", flush=True)
shutdown_timer = threading.Thread(
target=shutdown,
daemon=True
)
shutdown_timer.start()
# Cancel shutdown
elif not (button_292 and button_293):
shutdown_timer = None
Launching Gambatte Gameboy Emulator From Terminal
retroarch -L
// if installed on linux mint using
// sudo apt install libretro-gambatte
// then it will probably be at:
/usr/lib/x86_64-linux-gnu/libretro/gambatte_libretro.so
// full command for me:
retroarch -L /usr/lib/x86_64-linux-gnu/libretro/gambatte_libretro.so /home/bweew/Documents/Syncthing/Lsdj/lsdj.gb
f for full screen f1 for settings like changing the key maps
You can make it a bash script
#!/bin/bash
retroarch -L /usr/lib/x86_64-linux-gnu/libretro/gambatte_libretro.so "$HOME/Documents/Syncthing/Lsdj/lsdj.gb"
chmod +x ~/lsdj.sh
//on raspberry pi 3 64 bit os
#!/bin/bash
retroarch -L /usr/lib/aarch64-linux-gnu/libretro/gambatte_libretro.so lsdj.gb
Wednesday, August 26, 2026
maj7b5 and 7sus4b5 inversions
1 4 b5 b7
1 b9 4 5
1 3 b5 7
1 2 5 b6
1 3 b5 7
1 2 5 b6
1 4 b5 b7
1 b9 4 5
Tuesday, August 25, 2026
python gba compilation builder scripts
python scripts to build gba roms, emulators not included
Saturday, August 22, 2026
mediaboy - great for making music roms on gameboy easily
the music export is the most useful.
says it can do .mod and .uge but its not working for me
the gif and video exports work in sameboy but not on my flashcart or in gearboy
pcm samplerate settings:
9198 hz -GBVP2 parity yeilds best quality
DMG gives clearer images than colour it seems to mangle the image with blocky pixels
to
use that with gameboy slideshow generator instead of that because it screws up the audio
making gb and gbc videos worked better flashed directly with gb flasher to a perfect dark cart instead of on the superchis gba sd cart also sameboy and bgb are more accurate usually but goomba breaks
renaming png and jpgs as numbers using bash
i=1; for f in *.png *.jpg *.jpeg; do [ -f "$f" ] || continue; mv -- "$f" "$i.${f##*.}"; ((i++)); done
Friday, August 21, 2026
How to make full size lsdj videos like aquallex
tommitytom, the maker of retroplug made this web player that lets you view all the screens of lsdj at once.
finally a linux native retroplug for lsdj
update has a standalone, vst2, vst3, clap and commandline version
also works with nes
use retroplug-cli lsdj-rom --split channels flag foroutput
Thursday, August 20, 2026
new renoise2 mod linux native with terminal use
might need to chmod after moving
the github mainpage for more infoflags for different cli options
renoise2mod song.xrns --type xm
renoise2mod song.xrns --type mod --ptmode hardware --ntsc
renoise2mod --help
keyd - key remapping on linux
I wanted to be able to switch layers or something to map keys in a different localization to be able to use extra shortcuts.
--renoise doesn't detect f13 - 24 making this whole thing useless for my case. but its still a nice key remapper
Installing
git clone https://github.com/rvaiya/keyd
cd keyd
make && sudo make install
sudo systemctl enable --now keyd
Finding Keystrokes
keyd monitor
Listing Available Keys
keyd list-keys
Editing The Config
sudo vim /etc/keyd/default.conf
reload config
sudo keyd reload
emergency reset
sudo keyd reload
emergency reset
backspace+escape+enter
Wednesday, August 12, 2026
gb studio custom waveforms
GB Studio: Advanced Custom Waveform Design for GBT Player
for making custom waves lsdj or hugetracker
sine: 79 bc de ef ff ee dc b9 75 43 21 10 00 11 23 45
tri: fe dc ba 98 76 54 32 10 01 23 45 67 89 ab cd ef
Wednesday, August 5, 2026
Friday, July 31, 2026
patching savestates into gb and gbc games
doesn't have every game but still cool
sudo apt install bsdiff on linux
the syntax:
bspatch YOURGAME.gbc OUTPUT.GBC THEPATCH.bsdiff
shell to unzip .7z and .zip and del the compressed
for f in *.7z *.zip; do [ -e "$f" ] && 7z x "$f" && rm "$f"; done
Thursday, July 30, 2026
play wavs on gba with gsm play
couldn't get in pogoshell
heres my bash script using sox to convert mp3s to wav and then open the converter
#!/bin/bash
for file in "$@"; do
output="${file%.*}.wav"
sox "$file" -r 22050 -c 1 -e gsm-full-rate "$output"
wine "ps2gsm_linker.exe"
done
gba multi cart and mod files made easy with pogoshell
for f in *.mod; do wine dcmp.exe "$f" "${f%.mod}.mdz" && rm -- "$f"; done
that uses dcmp from the tools dir to convert .mod to .mdz to save space but it sometimes screws them up really bad lol
Wednesday, July 29, 2026
nes music on gbc
player for .nsf files on gbc
nsf player got it working on linux using wine
deflemask nes exports arent working but nsfs from games work
makes gbc roms out of the music files
Fl studio to hardware chiptune - midi2dmf sms vgm and gb vgb
i use yamaha syxgx vst on master bus in fl set to midi recieve chan 2
put reaper jsfx midi filter to raise it by 2 octaves for sms
lowest note on sms is a0 in fl studio(oct 1 in deflemask)
lowest note on gameboy is c2 in fl studio (oct3 in deflemask
for gameboy ch 4 noise:
c6 is good for a hat
c5 is good for a snare
effect 08 is panning: 10 L 01 R
effect 10 selects wave of chan 3
effect 12 is duty cycle 00, 01, 02, 03
sweeping is only for pulse channel 1
13 sets sweep 14 00 sweeps up 14 01 sweeps down (a non zero value)
open the deflemask gb proj in furnace to save as
if you make a sms song in defle
- save as sms rom and play on gba in sms emulator
- export a 4 chan sms psg vgm and make a .gba rom
Another way is to just use .mod files
fl to midi to openmpt
pokemon ruby on the mother3 gba cart settings
in the patcher I used:
savetype
offspec 512k / 1m flash (for carts with a non standard flash chip)
enabled fake RTC
in flash gbx:
savetype: 512k FLASH (64kib)
dont need to keep the original rom like castlevania did, instead use the patched rom for everything
patched rom can load the saves. the original wont recognize the saves.
It says the last save was corrupted each time but it works fine
Monday, July 27, 2026
got gba mother3 cart to hold saves with lesserkuma's web patcher tool
seting the save type to Off-spec 512K/1M FLASH worked for me.1m flash(128kb) also worked.
when saving the screen hangs for a bit, looks frozen but the saves work.
dumping the gba cart back to the computer i couldn't save in mgba in castlevania. the fix: use the unpatched rom with the 1m flash (128kb) save backup. fixing saving on the mother3 cart breaks being able to save in the emulators everywhere else so just copy the save and use that with the original rom
building master system chiptunes onto a gba cart
you can make .vgm files in deflemask for sega master system and use this to make a gba rom
the sn76489 psg sounds only work not the fm stuff
Saturday, July 25, 2026
fixing gba flashcarts not being able to save
the mother3 cart i got flashes fine but wont save from within making goomba multicart of lsdj basically useless for now until I fix that battery issue
making gb multicarts for gba with goomba color
this fixes that problem of deflemask exporting single .gb songs and only being able to load 1 song onto a gbc cart. also great using multiple lsdjs with save states. had to use windows vm though it wouldnt build the roms in wine on linux for me.
goomba colorFriday, July 24, 2026
carillon fx hammer
found out the sfx in frasier fantasy were done with carillon editor.
Thursday, July 23, 2026
visual novel style gb carts
gb studio gallery - robert doman youtube tutorial on making art carts. how to swap tiles for page numbers in gb studio
Monday, July 20, 2026
Saturday, July 18, 2026
vmp mod player for gba
got it working in wine
wine cmd /c makeVMP.cmd
it runs in wine ok by opening up in dosbox? whatever it works.
way easier than my other method just drop the files in and run the command to build a rom
vmp 1.1 on gbadev.orgmy gba music rom on github
23 tracks playable on a cartridge
you can use the code to add your own tracker music to create your own albums.
youtube tutorial on making carts by flashing over the mother3 carts. 1/3 the price of superchis or everdrive clones but no guarantees as ali sellers dont advertise them as being rewritable
the 369 in 1 carts seem like a good option if you reflash them too
one of the best tutorials for working with these cartsive seen these on ali for around 20 and decided to just get a superchis for 10$ more
because these gba multicarts cant be played in emulators they have to be flashed to carts
Friday, July 17, 2026
Thursday, July 16, 2026
music for gba - devkitpro / butano
another way is to use maxmod
programming GBA audio - jacob helgason
gbc music player
for gbc i used hugetracker to export the the c for gbdk
Gameboy Programming in C - How to setup GBDK 2020 on Linux
Im looking into getting a rewritable gba card just to playback mod music since figuring this out
jacob helgason's git repoTHIS WORKS!! after installing devkitpro and maxmod just swap out your mod files and album covers and run make and you get a gba rom of your mod files!!!!!
Sunday, July 12, 2026
Friday, July 10, 2026
Wednesday, July 8, 2026
Tuesday, July 7, 2026
clumsy midi
it has 2 midi ports and a dac
for mt32-pi builds
found it looking for a 3.3v midi circuit
Saturday, June 27, 2026
mastersystem and nes on gba
that means you can probably run
risa and smsggdj on the gba with these emulators.
the gamegear version of smsggdj works on the gba
sunvox zt-2020
arduino blog post. seems fascinating earthbound objects video is off youtube I can't find it.
Friday, June 26, 2026
Sunday, June 21, 2026
Saturday, June 20, 2026
Friday, June 19, 2026
Thursday, June 18, 2026
Sunday, June 14, 2026
lsdj wave cruncher
defensemech's blog post about it
patch in your own wavetables for lsdj. lets you do more as a wavechannel than as a sample kit.
Saturday, June 13, 2026
run multiple lsdj instances in the browser
only works on desktop because you need to drag the roms in.
Friday, June 12, 2026
midi to lsdj js library
nvm it doesnt convert to a lsdjsng it just tells you how many chains and patterns to manually draw it in
arduinoboy max editor use 5.0.8 not 9
9 runs like garbage on linux and locks up use an old version instead
max runtime 5.0.8 for windows the arduinoboy max editorThursday, June 11, 2026
gameboy link cable breakout pcb board for 2$
solder on some 1x6 m, f, or double male headers
I think il be able to make an arduinoboy from a promicro with just this and a link cable.
tempted to get the hobbychop one for 70 cad but its alot of money.
they have a breakout for 6$ but its US I think with tarrifs and shipping its 20$ :(
looking at cutting up my link cable and crimping on some dupont M connectors.
Tuesday, June 9, 2026
droid pad
use android tablet like a gamepad to control linux using websockets
or ble droidpad companion
could trigger keyboard shortcuts
spessasynth web soundfont exitor
spessasus soundfont editor github
cant really use on phone but sounds like it could be interesting il have to check it out deeper. looking at android theres not any options i know of other than maybe somehow running polyphone in a linux emulator?
Monday, June 8, 2026
getting lsd working on lenovo m8
-
retroarch with gambatte core for lsdj. had trouble showing blank directory with some file permission sanboxing. put the rom in documents and use the
-
lgpt in ppsspp shows a black screen. you have to go into settingd and change the rendering resolution every time if its 1x set it to 2X. it resets every time
Sunday, June 7, 2026
FL studio getting multiple plugins to show at once
R click in the corner and set plugins to detatched
in settings: general, uncheck auto select linked modules
lsdj patcher
lets you load custom fonts and edit colour pallates. for fonts the y2k is classic but I like the DOS, Protracker, and Renoise themes for custom. for Colour pallate I went with the solarized dark and modded it to be more greyscale and with a dusty yellow cursor instead of purple.
it exports songs as lsdprj. For lsdjsng export I think I need to use retroplug instead.
best gb emulator on linux mint 22.2
went with v 3.75 ubuntu 22.04 version because sdl3 wasn't found on the lastest build.
mgba sound quality sucks, retroplug is ok but the overhead of it running within wine cranks my fan. This is the best emulator on linux iv've foudn so far. was thinking about getting gambatte but its only available on sourceforge and i don't trust like that.
lsdj converter in browser
I use retroplug on my computer to open lsdj songs and export .lsdjsng because it doesnt work with .sav alone
the lsdj wikion linux mint the web apps tool lets you launch websites like apps.
is LSDJ worth in in 2026?
3 things got me feeling like a time traveller from the year 2000.
- ska
- microkorg
- lsdj
We have alot more options now that didn't exist back then.
I didn't have a gameboy back then. I always wanted to get into lsdj.
the costs add up.
i got a free gba with no power adapter and a swollen battery.
- flashcart 23$
- battery 10$
- charge cable 7$ + linkcable
40ish bucks just to get lsdj running.
but I want midi out. lsdj patches lets you load lofi samples but that sucks and then gba
needs another audio dongle to get 3.5mm out.
the idea of a portable handheld battery powered tracker gets less portable when a million dongles and power supplys get reqired.
the idea of repurpusing a game console for music doesn't make sense when all these costs pile up.
2 options to get midi:
- arduinoboy prebuilt 70$
- arduino leonardo + midisheild on ali 20$
could potentially save 50$
- probably have to edit the arduinoboy firmware to get the pins correct
- have to cut up a link cable (hobbychops arduinoboy comes with edge connectors)
-build a case to mechanically support the custom cable so it doesn't rip off some things that make it easeir to deal with3>
-gearboy
-lsdj patcher
- lsdj song to midi conversion tool to get midi out
- retroplug for audio syncing route.
wish retroplug could do it all for me but on linux its not working idk why I cant get he midi out
gearboy + the midi conversion tool is enough for me to want to get gba running again. dont need to shell out 70$ for the arduino boy
Saturday, June 6, 2026
sfizz apt repo for raspberry pi
echo 'deb http://download.opensuse.org/repositories/home:/sfztools:/sfizz/Raspbian_12/ /' | sudo tee /etc/apt/sources.list.d/home:sfztools:sfizz.list
curl -fsSL https://download.opensuse.org/repositories/home:sfztools:sfizz/Raspbian_12/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_sfztools_sfizz.gpg > /dev/null
sudo apt update
sudo apt install sfizz
Friday, June 5, 2026
pi 3 hifi hat i3 config auto assigned workspaces for fluidsynth and renoise
exec --no-startup-id i3-msg 'workspace1; exec renoise'
exec --no-startup-id i3-msg 'workspace2; exec lxterminal -e ~Documents/sf2/fluid.sh'
set cadence to start jack on login so fluidsynth can run properly
jack bridges have alsa to pulse to jack
# Update software sources
sudo apt-get update
# Install required dependencies if needed
sudo apt-get install gpgv wget
# Download package file
wget https://launchpad.net/~kxstudio-debian/+archive/kxstudio/+files/kxstudio-repos_11.2.0_all.deb
# Install it
sudo dpkg -i kxstudio-repos_11.2.0_all.deb
Thursday, June 4, 2026
my fluidsynth alsa setup with m-vave bt and pico
gnome-terminal -- bash -c '
fluidsynth -C0 -R0 -l -a alsa -m alsa_seq -is -p "FluidSynth-GS" \
-o synth.polyphony=64 \
~/Documents/sf2/GeneralUser-GS.sf2 &
until aconnect -l | grep -q "FluidSynth-GS"; do sleep 0.5; done
aconnect "SINCO" "FluidSynth-GS" 2>/dev/null
aconnect "Pico" "FluidSynth-GS" 2>/dev/null
wait
exec bash
'
my pico sends msb lsb program changes to switch sounds. the m-vave bt im using with my microkorg to play soundfonts.
this script lets me auto connect everything
Sunday, May 31, 2026
clarinova hex midi messages
yamaha clavinova clp-820 functions
-
f7 3 is the function for
local control disabling -
f7 1 and 2 are midi send/ receive channels
-
f2 1 sets temperament
1 is Equal
2 maj
3 min
4 pythagorean
5 meantone
6 werkmeister
7 kirnberger
Friday, May 29, 2026
midi hex channel message for drums
in loopy pro ndercustom midi messages
to pull up the drums:
B0 00 78 B0 20 00 C0 00
i forgot the last 00 at the end which is why it didn't work before.
Instrument MIDI Command
piano B0 00 00 B0 20 00 C0 01
bass B0 00 00 B0 20 00 C0 21
jazz-gt B0 00 00 B0 20 00 C0 1A
brass B0 00 00 B0 20 00 C0 3d
strings B0 00 00 B0 20 00 C0 30
tonewheelorg B0 00 00 B0 20 00 C0 10
clav B0 00 00 B0 20 00 C0 07
trombone B0 00 00 B0 20 00 C0 39
mute gt B0 00 00 B0 20 00 C0 1c
choir B0 00 00 B0 20 00 C0 34
barisax B0 00 00 B0 20 00 C0 43
Thursday, May 28, 2026
yamaha cbx-k1 stuck notes fix
press shift + tempo + 0 + enter to disable the external midi clock
had a similar problem with microkorg giving errors when trying to send patches over midi cable. I think it just sends too much data at once and gets garbled so this reduces that significantly.
Wednesday, May 27, 2026
Tuesday, May 26, 2026
Monday, May 25, 2026
digiwaves decent sampler
made an insturment for the decentsampler vst that can also run on ios. Its 64 digital waves you can swap between using the modwheel. If you rename the .dslibrary file to zip and extract there is a .sfz mapping which uses the same samples but maps them differently. The soundfont has no UI but its got a lowpass filter and amp and filter egs that correspond to the mk. The sfz is monophonic while the decentsampler patch is polyhonic.
Friday, May 22, 2026
Microkorg Lua Synth Definition For Renoise Guru Plugin
Guru lets you load presaved mappings for controlling hardware synths
so you dont need to reinvent the wheel and map everything out each time
on linux the scripts go in somewhere in /.config/renoise/scripts/tools
The Renoise Tool Guru
return SynthDefinition {
id = "korg_microkorg",
name = "Microkorg",
author = "Bweew",
Section {
Group {
name = "Filter",
Parameter { id="filter_type", name="Type", type="cc", number=83 },
Parameter { id="cutoff", name="Cutoff", type="cc", number=74 },
Parameter { id="resonance", name="Resonance", type="cc", number=71 },
Parameter { id="filter_eg_int", name="Filt EG", type="cc", number=79 },
Parameter { id="kbd_track", name="KBD Track", type="cc", number=85 }
},
Group {
name = "Filter EG",
Parameter { id="feg_attack", name="Attack", type="cc", number=23 },
Parameter { id="feg_decay", name="Decay", type="cc", number=24 },
Parameter { id="feg_sustain", name="Sustain", type="cc", number=25 },
Parameter { id="feg_release", name="Release", type="cc", number=26 }
},
Group {
name = "Amp EG",
Parameter { id="aeg_attack", name="Attack", type="cc", number=73 },
Parameter { id="aeg_decay", name="Decay", type="cc", number=75 },
Parameter { id="aeg_sustain", name="Sustain", type="cc", number=70 },
Parameter { id="aeg_release", name="Release", type="cc", number=72 }
},
Group {
name = "Delay",
Parameter { id="delay_time", name="Time", type="cc", number=13 },
Parameter { id="delay_depth", name="Depth", type="cc", number=94 }
},
Group {
name = "Mod FX",
Parameter { id="mod_speed", name="Speed", type="cc", number=12 },
Parameter { id="mod_depth", name="Depth", type="cc", number=93 }
},
Group {
name = "Mixer",
Parameter { id="osc1_level", name="Osc1 Level", type="cc", number=20 },
Parameter { id="osc2_level", name="Osc2 Level", type="cc", number=21 },
Parameter { id="noise_level", name="Noise Level", type="cc", number=22 }
},
Group {
name = "Osc 1",
Parameter { id="osc1_wave", name="Wave", type="cc", number=77 },
Parameter { id="osc1_ctrl1", name="Ctrl 1", type="cc", number=77 },
Parameter { id="osc1_ctrl2", name="Ctrl 2", type="cc", number=15 }
},
Group {
name = "Osc 2",
Parameter { id="osc2_wave", name="Wave", type="cc", number=78 },
Parameter { id="osc2_mod", name="Osc Mod", type="cc", number=82 },
Parameter { id="osc2_semi", name="Semitone", type="cc", number=18 },
Parameter { id="osc2_tune", name="Tune", type="cc", number=19 }
},
Group {
name = "LFO 1",
Parameter { id="lfo1_wave", name="Wave", type="cc", number=87 },
Parameter { id="lfo1_freq", name="Frequency", type="cc", number=27 }
},
Group {
name = "LFO 2",
Parameter { id="lfo2_wave", name="Wave", type="cc", number=88 },
Parameter { id="lfo2_freq", name="Frequency", type="cc", number=76 }
},
Group {
name = "V Patch",
Parameter { id="vpatch1", name="P1 Int", type="cc", number=28 },
Parameter { id="vpatch2", name="P2 Int", type="cc", number=29 },
Parameter { id="vpatch3", name="P3 Int", type="cc", number=30 },
Parameter { id="vpatch4", name="P4 Int", type="cc", number=31 }
}
}
}
Saturday, May 16, 2026
Renoise Midi Hardware Sampler Tool
been using this to sample the microkorg into renoise
Thursday, May 14, 2026
Microkorg Dashboard
Using the Dashboard Plugin In FL studio 10 I made a control surface for the Microkorg that has everything on a single page unlike the fruity midi out plugin that splits everything across several pages. I like it all on one because I find it easier to see everything and map them to hardware controllers or automate. Dropbox link
Thursday, April 9, 2026
Giant Steps
| Key | Giant Steps |
|---|---|
| G | B D7 | G Bb7 | Eb | Am7 D7 | G Bb7 | Eb F#7 | B | Fm7 Bb7 | Eb | Am7 D7 | G | C#m7 F#7 | B | Fm7 Bb7 | Eb | C#m7 F#7 |
| Ab | C Eb7 | Ab B7 | E | Bbm7 Eb7 | Ab B7 | E G7 | C | F#m7 B7 | E | Bbm7 Eb7 | Ab | Dm7 G7 | C | F#m7 B7 | E | Dm7 G7 |
| A | C# E7 | A C7 | F | Bm7 E7 | A C7 | F G#7 | C# | Gm7 C7 | F | Bm7 E7 | A | D#m7 G#7 | C# | Gm7 C7 | F | D#m7 G#7 |
| Bb | D F7 | Bb Db7 | Gb | Cm7 F7 | Bb Db7 | Gb A7 | D | Abm7 Db7 | Gb | Cm7 F7 | Bb | Em7 A7 | D | Abm7 Db7 | Gb | Em7 A7 |
| B | D# F#7 | B D7 | G | C#m7 F#7 | B D7 | G A#7 | D# | Am7 D7 | G | C#m7 F#7 | B | Fm7 A#7 | D# | Am7 D7 | G | Fm7 A#7 |
| C | E G7 | C Eb7 | Ab | Dm7 G7 | C Eb7 | Ab B7 | E | Bbm7 Eb7 | Ab | Dm7 G7 | C | F#m7 B7 | E | Bbm7 Eb7 | Ab | F#m7 B7 |
| Db | F Ab7 | Db E7 | A | Ebm7 Ab7 | Db E7 | A C7 | F | Bm7 E7 | A | Ebm7 Ab7 | Db | Gm7 C7 | F | Bm7 E7 | A | Gm7 C7 |
| D | F# A7 | D F7 | Bb | Em7 A7 | D F7 | Bb C#7 | F# | Cm7 F7 | Bb | Em7 A7 | D | G#m7 C#7 | F# | Cm7 F7 | Bb | G#m7 C#7 |
| Eb | G Bb7 | Eb Gb7 | Bbm | Fm7 Bb7 | Eb Gb7 | Bbm D7 | G | C#m7 F#7 | Bbm | Fm7 Bb7 | Eb | Am7 D7 | G | C#m7 F#7 | Bbm | Am7 D7 |
| E | G# B7 | E G7 | C | F#m7 B7 | E G7 | C D#7 | G# | Dm7 G7 | C | F#m7 B7 | E | A#m7 D#7 | G# | Dm7 G7 | C | A#m7 D#7 |
| F | A C7 | F Ab7 | Db | Gm7 C7 | F Ab7 | Db E7 | A | Ebm7 Ab7 | Db | Gm7 C7 | F | Bm7 E7 | A | Ebm7 Ab7 | Db | Bm7 E7 |
| F# | A# C#7 | F# A7 | D | G#m7 C#7 | F# A7 | D F7 | A# | Em7 A7 | D | G#m7 C#7 | F# | Cm7 F7 | A# | Em7 A7 | D | Cm7 F7 |
Monday, March 9, 2026
deflemask triplets in 12 lines instead of 8
with a pattern length of 48 with each beat being 4 lines
set the speeds both to 2: 09-02, and 0F-02 to align with 8th note quarter notes
deflemask triplets using speed commands
in 2 columns set 09 01 and 0f 01
then to reset back 09 03 0f 03
that lets you sequence like regular quarter notes across 8 lines
Thursday, March 5, 2026
Timing Systems in the History of Music Trackers
In the history of music trackers (especially on early computers like the Commodore 64 and Commodore Amiga), timing systems evolved because different hardware offered different ways to measure time. Terms like PPQN, CIA, VBlank, NTSC, PAL, and LPB refer to how trackers scheduled musical events such as notes and effects.
1. VBlank Timing (Early Trackers)
VBlank = Vertical Blank interrupt
Early trackers synchronized playback with the screen refresh interrupt.
- The video chip triggers an interrupt every frame.
- The music routine runs once per frame.
Frame rates depended on TV standards
- PAL: 50 Hz (50 interrupts per second)
- NTSC: ~60 Hz (59.94 interrupts per second)
These standards come from analog television systems used by machines like the Commodore 64 and early Amiga 500.
Effect on tracker timing
A tracker row advanced every N frames.
Example (classic Amiga trackers):
Speed = 6
PAL 50 Hz
Row duration:
6 frames / 50 Hz = 120 ms per row
Pros
- Simple
- Stable with graphics
Cons
- Different speed on PAL vs NTSC
- Limited timing resolution
This system was used in early trackers such as Soundtracker (1987).
2. CIA Timer Timing (Amiga Precision Timing)
The Commodore Amiga had CIA chips (Complex Interface Adapters) with programmable timers.
Later trackers used CIA Timer A instead of VBlank interrupts.
Why?
CIA timers allow arbitrary interrupt rates, independent of video refresh.
Example:
Timer frequency ≈ 709379 Hz / divider
This allowed:
- More precise BPM
- Same playback speed on PAL and NTSC
Trackers such as ProTracker used this technique.
Pros
- Hardware accurate timing
- Independent from video
- Higher precision
Cons
- Slight CPU overhead
- Required careful hardware programming
3. LPB (Lines Per Beat)
LPB = Lines Per Beat
This concept appeared in later trackers during the PC era.
Trackers such as FastTracker II and Impulse Tracker separated:
- tempo (BPM)
- pattern resolution
LPB defines how many rows equal one beat.
Example:
BPM = 125
LPB = 4
Meaning:
- 4 rows per beat
- 16 rows per bar (4/4)
This makes timing more musical and flexible.
4. PPQN (Pulses Per Quarter Note)
PPQN = Pulses Per Quarter Note
This timing concept comes from MIDI sequencing rather than trackers.
Common MIDI timing resolutions:
24 PPQN
96 PPQN
480 PPQN
960 PPQN
Higher PPQN values give finer timing resolution.
Later tracker-inspired software and modern trackers adopted similar high-resolution internal clocks.
Examples include modern trackers and DAWs such as Renoise and OpenMPT.
5. Relationship Between These Systems
| System | Used In | Resolution | Hardware Dependence |
|---|---|---|---|
| VBlank | early Amiga / C64 trackers | low | tied to video |
| PAL / NTSC | video standards | defines frame rate | hardware |
| CIA timer | later Amiga trackers | medium | hardware timer |
| LPB | PC trackers | high | software |
| PPQN | MIDI / DAWs | very high | software |
6. Typical Classic Tracker Timing Formula
For ProTracker-style trackers:
row_time = speed × tick_time
Where:
tick_time = 2.5 / BPM seconds
Example:
speed = 6
BPM = 125
Tick time:
2.5 / 125 = 0.02 s
Row time:
6 × 0.02 = 0.12 s
This produces the famous 125 BPM / speed 6 groove used in many Amiga modules.
7. Historical Timeline
1980s
- VBlank timing
- PAL/NTSC differences
- Soundtracker era
Early 1990s
- CIA timer timing
- ProTracker era
Mid 1990s
- PC trackers adopt BPM + speed system
Late 1990s
- LPB introduced
2000s+
- PPQN-style internal clocks
Summary
- PAL / NTSC → video refresh standards
- VBlank → music tick tied to screen frame
- CIA timer → programmable hardware timer interrupts
- LPB → musical resolution (rows per beat)
- PPQN → MIDI timing resolution
understaning clock speeds in sunvox and deflemask
deflemask default is ntsc basetime 1 = 150bpm
sunvox default is 125 6tpl
- to match deflemask to sunvox:
set to pal, increase basetime from 1 to 2
- to match sunvox to defle:
lower tpl from 6 to 3 and increase bpm to 150
for quarter note (8 lines) to be equivalent
sunvox at 6tpl is half the speed of deflemask pal basetime 1.
increasing basetime lowers the speed
while decreasing the tpl increases the speed
triplet tuplets in trackers
-
32 lines
half note triplet
00, 11, 21 -
16 lines
quarter note triplet
00, 05, 11 -
8 lines
eighth note triplet
00, 03, 05 -
4 lines
sixteenth note triplet
00, 01, 03 -
64 lines
1 bar triplet
00, 21, 43
1/16 note | 4 lines | 00 01 03
1/8 note | 8 lines | 00 03 05
1/4 note | 16 lines | 00 05 11
1/2 note | 32 lines | 00 11 21
1 bar | 64 lines | 00 21 43
Monday, March 2, 2026
converting long samples to 16bit 8000khz for deflemask using sox
for f in *.wav; do sox "$f" -r 8000 -e signed-integer -b 16 "${f%.wav}_16bit.wav"; done
Wednesday, February 25, 2026
.bashrc function to toggle function keys from being media keys to being regular fn keys on linux mint
fnkeys() {
FN="/sys/module/hid_apple/parameters/fnmode"
if [ ! -f "$FN" ]; then
echo "Fn control not available."
return 1
fi
current=$(cat "$FN")
if [ "$current" -eq 2 ]; then
sudo sh -c "echo 1 > $FN"
echo "Switched to media keys (fnmode=1)"
else
sudo sh -c "echo 2 > $FN"
echo "Switched to F1–F12 keys (fnmode=2)"
fi
}
Saturday, February 21, 2026
fasttracker2 on ipad
under:
- quickmenu
- core options
- general
force output fps set to 60 stopped the stuttering
Thursday, February 19, 2026
Tuesday, February 17, 2026
reaper midi looping custom action
combine these 7 actions into a macro you can trigger with a shortcut or add to a toolbar
- Transport: Record
- Take: Switch items to previous take
- Take: Crop to active take in items
- Track: Duplicate tracks
- Item: Select all items in track
- Item: Remove items
- Transport: Record
Monday, February 16, 2026
you can toggle between mt32 and soundfonts using sysex in mt32-pi
to find ports use aplaymidi or aconnect then use this as the info for the -p option
aplaymidi -l
-g to change to soundfont
mt32-pi-ctl -p 128:0 -g
-s to change --soundfont specify the number
mt32-pi-ctl -p 128:0 -s 1
-m to change to mt32
mt32-pi-ctl -p 128:0 -m
-b to change romset: old, new, cm32l
mt32-pi-ctl -p 128:0 -b cm32l
Saturday, February 14, 2026
making midis in reaper and playing them over udp
#!/usr/bin/env python3
import time
from socket import AF_INET, SOCK_DGRAM, socket
import mido
import threading
import signal
import sys
# MT32-Pi UDP target
UDP_HOST = "192.168.0.100"
UDP_PORT = 1999
sock = socket(AF_INET, SOCK_DGRAM)
# MIDI file path
midi_file = "/home/bweew/Desktop/midi_export.mid"
# Shared variable for dynamic CC91
dynamic_cc91 = [64] * 16 # default reverb level per channel
# Track currently sounding notes per channel
active_notes = {ch: set() for ch in range(16)}
def cc91_input():
global dynamic_cc91
while True:
try:
raw = input("Set CC91 (format: channel,value e.g., 0,80): ")
ch, val = map(int, raw.split(','))
if 0 <= ch < 16 and 0 <= val <= 127:
dynamic_cc91[ch] = val
sock.sendto(bytes([0xB0 | ch, 91, val]), (UDP_HOST, UDP_PORT))
except Exception:
print("Invalid input. Format: channel,value")
def send_note_offs():
for ch, notes in active_notes.items():
for note in notes:
sock.sendto(bytes([0x80 | ch, note, 0]), (UDP_HOST, UDP_PORT))
for ch in range(16):
sock.sendto(bytes([0xB0 | ch, 123, 0]), (UDP_HOST, UDP_PORT))
def signal_handler(sig, frame):
print("\nAborting playback, sending note-offs...")
send_note_offs()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
# start thread for dynamic CC91 input
threading.Thread(target=cc91_input, daemon=True).start()
print(f"Playing {midi_file} to {UDP_HOST}:{UDP_PORT}... (Ctrl-C to stop)")
while True:
mid = mido.MidiFile(midi_file)
active_notes = {ch: set() for ch in range(16)} # reset notes
for msg in mid:
time.sleep(msg.time)
if msg.type == 'note_on':
sock.sendto(bytes([0x90 | msg.channel, msg.note, msg.velocity]), (UDP_HOST, UDP_PORT))
if msg.velocity > 0:
active_notes[msg.channel].add(msg.note)
else:
active_notes[msg.channel].discard(msg.note)
elif msg.type == 'note_off':
sock.sendto(bytes([0x80 | msg.channel, msg.note, msg.velocity]), (UDP_HOST, UDP_PORT))
active_notes[msg.channel].discard(msg.note)
elif msg.type == 'program_change':
sock.sendto(bytes([0xC0 | msg.channel, msg.program]), (UDP_HOST, UDP_PORT))
elif msg.type == 'control_change':
val = dynamic_cc91[msg.channel] if msg.control == 91 else msg.value
sock.sendto(bytes([0xB0 | msg.channel, msg.control, val]), (UDP_HOST, UDP_PORT))
send_note_offs()
print("Looping playback...")
Friday, February 13, 2026
playing soundfonts on mt32-pi over udp
my udp.py
#in the config disabled dhcp and set fixed ip
cranked the gain from 0.2 to 0.8, disabled reverb and chorus
(you need to edit the .cfg files for individual soundfonts because those will overide the main config
didn't get rtp working but udp works pretty ok.
midi over wifi can sometimes be a bit wonky but the latency isnt bad.
was hoping to make use of my other midi keyboard in my closet but the notes would get stuck
#!/usr/bin/env python3
from signal import SIGINT, signal
from socket import AF_INET, SOCK_DGRAM, socket
from threading import Event
from rtmidi import MidiIn
UDP_HOST = "192.168.0.100"
UDP_PORT = 1999
MIDI_PORT_NAME = "mt32-pi UDP socket"
sock = socket(AF_INET, SOCK_DGRAM)
midiin = MidiIn()
midiin.open_virtual_port(MIDI_PORT_NAME)
signal(SIGINT, lambda *_: exit(1))
print(
f"Listening for MIDI on port '{MIDI_PORT_NAME}' and sending to"
f" {UDP_HOST}:{UDP_PORT} (Ctrl-C to exit)..."
)
midiin.ignore_types(False, False, False)
midiin.set_callback(lambda msg, _: sock.sendto(bytes(msg[0]), (UDP_HOST, UDP_PORT)))
Event().wait()
Sunday, February 8, 2026
fluidsynth for octamed
you can make midi templates for sunvox and fl studio also
here i use linux mint autostart to run the bash script
and run the render command
- path must be like /home/user/Desktop/ not
~/Desktop/
also drag and drop wont work
it will try to open in celluloid so you must type it out.
#fluidsynth from terminal
fluidsynth -is -p "FluidSynth-GS" -g 0.7 ~/Documents/sf2/GeneralUser-GS.sf2
#bash for autostart
#!/bin/bash
gnome-terminal -- bash -c 'fluidsynth -is -p "FluidSynth-GS" -g 0.7 ~/Documents/sf2/GeneralUser-GS.sf2; exec bash'
#rendering
read -e -p "MIDI file path: " f; f=${f%\"}; f=${f#\"}; fluidsynth -is -p "FluidSynth-GS" -g 0.7 -R 0 -C 0 -F ~/Desktop/"$(basename "$f" .mid)".wav ~/Documents/sf2/GeneralUser-GS.sf2 "$f"; echo "Rendered ~/Desktop/$(basename "$f" .mid).wav"
# using jack instead for cadence to record in audacity
#!/bin/bash
gnome-terminal -- bash -c 'fluidsynth -a jack -m alsa_seq -is -p "FluidSynth-GS" -g 0.7 ~/Documents/sf2/GeneralUser-GS.sf2; exec bash'
# optional monitoring to find port use jack_lsp
jack_connect "FluidSynth-GS:audio_L" "system:playback_1"
jack_connect "FluidSynth-GS:audio_R" "system:playback_2"
# jack and monitoring
#!/bin/bash
gnome-terminal -- bash -c '
fluidsynth -a jack -m alsa_seq -is -p "FluidSynth-GS" -g 0.7 ~/Documents/sf2/GeneralUser-GS.sf2 &
until jack_lsp | grep -q "fluidsynth:left"; do sleep 0.5; done
jack_connect fluidsynth:left system:playback_1
jack_connect fluidsynth:right system:playback_2
wait
exec bash'
Wednesday, February 4, 2026
Tuesday, February 3, 2026
samplerbox raspberry pi
program your own screenless rompler. write is disabled on the os making it safe for the sd to be undamaged by unplugging. (idk about the usb with the sounds though? I guess its fine as long as your not in the middle of transferring files)
defle distilled
made a cheatsheet for learning deflemask.
- distilled 53 pages down to 1 front and back.
Monday, February 2, 2026
deflemask
running deflemask on ipad with a logitech k480
the keyboard os modes matter bigtime!
the 2 modes: pc, i.
in I (ios mode)
- cmd h for home, cmd tab for switching apps
in pc mode
- fn left / right = home / end
- fn up / down = pgup pgdown
turn off follow
in the right panel so you can use f7 to play from line
otherwise it keeps going. ESSENTIAL!
- ctrl 123456789 mute channels
- ctrl left / right switch tracks
- ctrl w/e shrink expand
- ctr b = paste and mix (ctrl v is regular paste)
- switching octave is done on screen because /* is numpad only
- note off is tab
- interpolate = ctrl i
- transposing = ctrl f1/f2,f3/f4
work from the top
instead of working from 0-64 top down work horizontally in 16 line chunks to make use of the f5 playback and f7 playbacks for the best of both worlds. copy and paste each 1/4 pattern horizontally and ctrl 123456789 to mute the previous until you complete all four quarters of a pattern to re assemble them.
Tuesday, January 27, 2026
splitting files for compression for fat32
the way to go would have been to use gparted to format to exfat
fat32 has a 4gb file limitation
// for rar
rar a -r -v3900m archive.rar /path/to/folder/
// for 7zip
7z a -v3900m archive.7z /path/to/folder/
// for zip
zip -r -s 3900m archive.zip /path/to/folder/
Sunday, January 18, 2026
sunchop - a chopper for sunvox
sunchop on the sunvox forum eufex made this in pixilang. the demo vid said single sampler pitch mapping is broken but it does work for individual samples for each chop. (eggsem tool in renoise had the same problem) I prefer xrns2mod because then i can get then all self contained in one insturment.
The xrns2mod tool didn't work o. my other computer though so this is when it would come in handy.
Thursday, January 15, 2026
best way to make drum kits in subvox
running the cli version in a vm on windows actually is great.
The linux version kept saying my project was too new or something and the renoise tool version would render a blank xm.
My old method of building kits was to use openmpt to create xis out of sfzs made in renoise of groups of samples but the tuning would be off.
the eggsem tool to convert xrns to xm works at getting the pattern data and simple 1 shot insturments but it would mangle pitches on multisampled ones so the xrns2mod cli tool is my new go to method for now.
Deflemask chip tracking strategies
It lacks some important keyboard shortcuts i use all the time in Renoise because of this i've mostly used it as a sound module for ym2612 sounds sampling it and not dealing with the sequencer but i've figured out a few tricks recently to get around that.
no f9-f12 jump 1/4 way through or play from line and return. the play from line in Deflemask just keeps going from where you left off and play pattern always jumps to the start.
my work around is do each 16 ticks on its own channel and mute each one as i move 1/4 across.
this feels similar to using block loop in Renoise.
Another alternative is to use pattern break commands to jump to line zero. it reminds me of clipping in as you climb higher up a wall.
Saturday, January 10, 2026
how to move nested wavs
Downloaded some samples that came packed into separate folders. This is annoying because there is only 1 wav in each folder making it a hassle to browse.
heres the fix:
mv -n */*.wav .
then to remove the folders
rmdir */
xm export from renoise
xrns2mod required something called bassmidi library which needed a license to run? tried and gave up this one actually worked for me but the pitches get screwed up in multi samples. still way better than nothing.
Sunday, January 4, 2026
sunvox midi automations
in the cc column it starts at 80
so cc1 is actually 81 (also its in hex).
- 91 for reverb is db
- 74 brightness (cutoff)is ca
- 71 timbre (res) is c7
the interpolator tool can quickly create ramps for filter sweeps at a set interval like a tempo synced lfo
Saturday, January 3, 2026
ringmod sidechaining easy and free
kilohearts compactor makes that nee ring mod side chaining technique easy
Friday, January 2, 2026
chopping samples in sunvox
Easily miss this setting because its hidden in the edit menu under misc as resample new length. set the number of lines to the end of your sample and it will adjust the pitch to play out to that length.
Then you can use the 7 effect to chop on offsets of 8000
snare on 4000 etc..
making kits in sunvox
- add your samples
- connect a multisynth
- options 75 7F
that sets multisynth to c5 and divides output midi note by number of inputs to spread the sounds across the keys. loading sounds it's probably easiest to use milkytracker to create .xi files and save them as meta modules.

