Pages

Wednesday, February 26, 2025

sending midi cc 64 on press and cc 65 when held for 1 second - piano sustain pedal midi scripting

For use in Reapers super 8 looper. Experimented with using on release instead of on press (like in loopy pro) but the travel distance of this piano sustain pedal is too large to be accurate so I had to remove my double and triple tap functions, still awesome being able to double your midi inputs for 0$ was hoping to get all the functions mapped though.. looking at the m vave chocolate foot switch though looks pretty cool for 50$


import time
import rtmidi
from rtmidi.midiconstants import CONTROL_CHANGE

# Configuration - Customized for single pedal operation
SUSTAIN_CC = 64          # Default sustain pedal CC - Used for both functions
RECORD_TOGGLE_CC = 64    # Record/Play/Overdub (same as sustain)
CLEAR_LOOP_CC = 65       # Clear loop CC to be triggered by holding sustain

MIDI_CHANNEL = 0         # MIDI channel (0-15)
HOLD_THRESHOLD = 1.0     # Time in seconds required to hold sustain to clear loop

class LooperPedalController:
    def __init__(self):
        # Set up MIDI input/output
        self.midi_in = rtmidi.MidiIn()
        self.midi_out = rtmidi.MidiOut()
        
        # State variables
        self.is_recording = False
        self.is_playing = False
        self.pedal_down = False
        self.press_start_time = 0
        self.action_triggered = False
        
        self.setup_midi()
        
    def setup_midi(self):
        # List available ports
        in_ports = self.midi_in.get_ports()
        out_ports = self.midi_out.get_ports()
        
        print("Available MIDI input ports:")
        for i, port in enumerate(in_ports):
            print(f"  {i}: {port}")
            
        print("Available MIDI output ports:")
        for i, port in enumerate(out_ports):
            print(f"  {i}: {port}")
        
        # Let user select ports
        in_port = int(input("Select MIDI input port number: "))
        out_port = int(input("Select MIDI output port number: "))
        
        # Open ports
        self.midi_in.open_port(in_port)
        self.midi_out.open_port(out_port)
        
        # Set callback
        self.midi_in.set_callback(self.midi_callback)
        
    def midi_callback(self, event, data=None):
        message, delta_time = event
        
        # Process Control Change messages for the sustain pedal
        if len(message) == 3 and message[0] == CONTROL_CHANGE | MIDI_CHANNEL and message[1] == SUSTAIN_CC:
            value = message[2]
            current_time = time.time()
            
            if value >= 64:  # Pedal down
                self.handle_pedal_down(current_time)
            else:  # Pedal up
                self.handle_pedal_up(current_time)
    
    def handle_pedal_down(self, current_time):
        # Record when the pedal was pressed
        self.press_start_time = current_time
        self.pedal_down = True
        self.action_triggered = False
        
    def handle_pedal_up(self, current_time):
        duration = current_time - self.press_start_time
        self.pedal_down = False
        
        # If action was already triggered (by hold), do nothing more
        if self.action_triggered:
            return
            
        # If pedal was pressed briefly (not held), trigger record/play/overdub
        if duration < HOLD_THRESHOLD:
            self.handle_record_toggle()
    
    def handle_record_toggle(self):
        # Toggle between record, play, and overdub
        print("Quick press - Record/Play/Overdub toggle")
        self.send_cc(RECORD_TOGGLE_CC, 127)
        time.sleep(0.05)
        self.send_cc(RECORD_TOGGLE_CC, 0)
        
        # Update internal state (for display purposes)
        if not self.is_recording and not self.is_playing:
            self.is_recording = True
            print("State: Recording")
        elif self.is_recording:
            self.is_recording = False
            self.is_playing = True
            print("State: Playing")
        else:
            self.is_recording = True
            print("State: Overdubbing")
    
    def handle_clear_loop(self):
        print("Hold detected - Clearing loop (sending CC 65)")
        self.send_cc(CLEAR_LOOP_CC, 127)
        time.sleep(0.05)
        self.send_cc(CLEAR_LOOP_CC, 0)
        
        # Update internal state
        self.is_recording = False
        self.is_playing = False
        print("State: Cleared")
        self.action_triggered = True
        
    def send_cc(self, cc_number, value):
        message = [CONTROL_CHANGE | MIDI_CHANNEL, cc_number, value]
        self.midi_out.send_message(message)
    
    def run(self):
        print("\n=== Single Pedal Looper Controller ===")
        print(f"- Quick press CC {SUSTAIN_CC}: Record/Play/Overdub")
        print(f"- Hold CC {SUSTAIN_CC} for {HOLD_THRESHOLD}+ sec: Clear loop (sends CC {CLEAR_LOOP_CC})")
        print("=====================================\n")
        print("Current state: Stopped")
        
        try:
            # Keep the script running
            while True:
                time.sleep(0.05)  # Check frequently for responsive hold detection
                
                # Check for hold while pedal is down
                if self.pedal_down and not self.action_triggered:
                    current_time = time.time()
                    if current_time - self.press_start_time >= HOLD_THRESHOLD:
                        self.handle_clear_loop()
                        
        except KeyboardInterrupt:
            print("Exiting...")

if __name__ == "__main__":
    controller = LooperPedalController()
    controller.run()

Tuesday, February 25, 2025

fix i3 window focus

this has been bugging me forever. When I would try to play synths using the computers built in keyboard in renoise the window focus would steal keyboard input. add this to your i3 config to disable.


focus_follows_mouse yes|no

Saturday, February 22, 2025

4op to dx7 conversion project

I was looking for 2 op fm patches similar to the adlib soundblaster or my pss170 and trying to see if anybody converted the sysex to dx7 format and came across this project. 4op2dx7
super stoked they have the fb01!!! no longer need to use deflemasks fb01 patches. I wanted to like it. Any tracker that doesn't have a hot key to jump to next note/ cyclic shift and play from line is too tedious. I used to have to do a work around where I would route midi from fl and use pulse audios loopback to re sample it in audacity.

Friday, February 21, 2025

python to send midi ccs using usb snes gampad

super 8 preset and logitech f310 to cc mapping

instead of starting on cc 64 I started mapping from cc65 so I could use my piano sustain pedal to trigger loops and use the dpad to select the loops.
x doubles, a halves the length.
the middle guide (logitech) button dumps the loops to the timeline (add to project)
L button kills all the loops

Originally I did it with this 2$ ali express gamepad, later I wanted to get it working with this logitech ps1 style controller that has more buttons (more possibilities).

dropbox link to snes-midicc.py

Using It within Super 8

my super 8 cc mapping

import pygame
import rtmidi
import time
import sys

def initialize_midi():
    midi_out = rtmidi.MidiOut()
    available_ports = midi_out.get_ports()
    
    if not available_ports:
        print("No MIDI output ports found. Creating virtual port 'Gamepad MIDI'...")
        midi_out.open_virtual_port("Gamepad MIDI")
    else:
        print("Available MIDI ports:", available_ports)
        midi_out.open_port(0)
        print(f"Connected to: {available_ports[0]}")
    
    return midi_out

def initialize_joystick():
    pygame.init()
    pygame.joystick.init()
    
    if pygame.joystick.get_count() == 0:
        print("No gamepad detected!")
        sys.exit(1)
    
    joystick = pygame.joystick.Joystick(0)
    joystick.init()
    print(f"Detected gamepad: {joystick.get_name()}")
    return joystick

def main():
    midi_out = initialize_midi()
    joystick = initialize_joystick()
    
    button_states = {
        'left': False,
        'right': False,
        'up': False,
        'down': False
    }
    
    prev_buttons = [False] * joystick.get_numbuttons()
    
    print("Gamepad to MIDI mapper running. Press Ctrl+C to exit.")
    
    try:
        while True:
            for event in pygame.event.get():
                if event.type == pygame.JOYAXISMOTION:
                    if event.axis == 0:  # Left/Right
                        if event.value < -0.5 and not button_states['left']:
                            midi_out.send_message([0xB0, 64, 127])
                            button_states['left'] = True
                        elif event.value > -0.5 and button_states['left']:
                            midi_out.send_message([0xB0, 64, 0])
                            button_states['left'] = False
                            
                        if event.value > 0.5 and not button_states['right']:
                            midi_out.send_message([0xB0, 66, 127])
                            button_states['right'] = True
                        elif event.value < 0.5 and button_states['right']:
                            midi_out.send_message([0xB0, 66, 0])
                            button_states['right'] = False
                            
                    elif event.axis == 1:  # Up/Down
                        if event.value < -0.5 and not button_states['up']:
                            midi_out.send_message([0xB0, 65, 127])
                            button_states['up'] = True
                        elif event.value > -0.5 and button_states['up']:
                            midi_out.send_message([0xB0, 65, 0])
                            button_states['up'] = False
                            
                        if event.value > 0.5 and not button_states['down']:
                            midi_out.send_message([0xB0, 67, 127])
                            button_states['down'] = True
                        elif event.value < 0.5 and button_states['down']:
                            midi_out.send_message([0xB0, 67, 0])
                            button_states['down'] = False
                
                elif event.type == pygame.JOYBUTTONDOWN:
                    button_num = event.button
                    cc_number = 68 + button_num
                    midi_out.send_message([0xB0, cc_number, 127])
                    prev_buttons[button_num] = True
                    
                elif event.type == pygame.JOYBUTTONUP:
                    button_num = event.button
                    cc_number = 68 + button_num
                    midi_out.send_message([0xB0, cc_number, 0])
                    prev_buttons[button_num] = False
                    
            time.sleep(0.001)
            
    except KeyboardInterrupt:
        print("\nExiting...")
    finally:
        midi_out.close_port()
        pygame.quit()

if __name__ == "__main__":
    main()

original attempt

starts from cc64 and up
blue X = cc69
red A = cc70
yellow b = cc71
green y = cc72
select = cc77
start = cc78
Lt = cc73
Rt = cc74
up/down = 0/ 126
left/right = 0/126
I want to fix this so each direction sends on its own cc as momentary but this is fine for now


import pygame
import mido

MIDI_CC_START = 64

# Initialize MIDI output
midi_out = mido.open_output(mido.get_output_names()[0])

def send_midi_cc(cc, value):
    msg = mido.Message('control_change', control=cc, value=value)
    midi_out.send(msg)

def main():
    pygame.init()
    pygame.joystick.init()
    
    if pygame.joystick.get_count() == 0:
        print("No gamepad detected.")
        return

    joystick = pygame.joystick.Joystick(0)
    joystick.init()
    
    print(f"Gamepad detected: {joystick.get_name()}")
    print("Press Ctrl+C to exit.")
    
    prev_axes = [0] * joystick.get_numaxes()
    prev_buttons = [0] * joystick.get_numbuttons()
    prev_hats = [(0, 0)] * joystick.get_numhats()
    
    try:
        while True:
            pygame.event.pump()
            
            # Read axis values
            axes = [joystick.get_axis(i) for i in range(joystick.get_numaxes())]
            for i, value in enumerate(axes):
                if abs(value) > 0.1 and abs(value - prev_axes[i]) > 0.01:
                    send_midi_cc(MIDI_CC_START + i, int((value + 1) / 2 * 127))  # Normalize to 0-127
                prev_axes[i] = value
            
            # Read button states
            buttons = [joystick.get_button(i) for i in range(joystick.get_numbuttons())]
            for i, pressed in enumerate(buttons):
                cc_number = MIDI_CC_START + 5 + i  # Buttons start after D-pad CCs
                if pressed and not prev_buttons[i]:
                    send_midi_cc(cc_number, 127)
                elif not pressed and prev_buttons[i]:
                    send_midi_cc(cc_number, 0)
                prev_buttons[i] = pressed
            
            # Read hat (D-pad) states (momentary, dedicated CCs)
            hats = [joystick.get_hat(i) for i in range(joystick.get_numhats())]
            for i, hat in enumerate(hats):
                left_cc = 64
                up_cc = 65
                right_cc = 66
                down_cc = 67
                
                if hat[0] == -1 and prev_hats[i][0] != -1:
                    send_midi_cc(left_cc, 127)
                elif hat[0] != -1 and prev_hats[i][0] == -1:
                    send_midi_cc(left_cc, 0)
                
                if hat[1] == 1 and prev_hats[i][1] != 1:
                    send_midi_cc(up_cc, 127)
                elif hat[1] != 1 and prev_hats[i][1] == 1:
                    send_midi_cc(up_cc, 0)
                
                if hat[0] == 1 and prev_hats[i][0] != 1:
                    send_midi_cc(right_cc, 127)
                elif hat[0] != 1 and prev_hats[i][0] == 1:
                    send_midi_cc(right_cc, 0)
                
                if hat[1] == -1 and prev_hats[i][1] != -1:
                    send_midi_cc(down_cc, 127)
                elif hat[1] != -1 and prev_hats[i][1] == -1:
                    send_midi_cc(down_cc, 0)
                
                prev_hats[i] = hat
            
            pygame.time.wait(100)  # Reduce CPU usage
    except KeyboardInterrupt:
        print("\nExiting...")
    finally:
        joystick.quit()
        pygame.quit()
        midi_out.close()

if __name__ == "__main__":
    main()

ym2612 dexed presets

sega genesis style patches for your dx7

ym2612 dexed on github

cool they work on ios in kq dixie! probably would work in fm8, and sytrus too then. I love plogues chipsynth md for ym2612 sounds. I haven't been able to run it on linux though. I heard on bluesky in q2 they ate planning on having linux betas ready by then so I'm excited. In the meantime Ive been using the rom dumped patches converted to tfi format from aly james lab's fm drive that ported the fb-01 patches to the megadrive. that 4 op sound is close to the genesis sound. I prefer that curated palate of sounds to having the millions of dx7 nameless electric piano patches. those collections are cumbersome and wading through those is exhausting.

Theres a sweet spot with having presets. too many or too few is bad. it takes alot of time to make sounds from scratch when tou already have a musical idea in your head and you want to get it out. Thats why having presets can be usefull. Too many and it feels like your drowning. spend more time comparing sound a with sound b and get nowhere.

Thursday, February 20, 2025

spectral compressor vst

its kind of ott but with wayyy more bands. can be used like soothe. the github link

Wednesday, February 19, 2025

studyflash - terminal flashcards

you can import cards from quizlet

  • import/export csv
  • edit using vim
  • install using pip

studyflash

git clone https://github.com/Alone2/studyFlash.git
sudo pip install ./studyFlash

Tuesday, February 18, 2025

Instajungle

Instajungle on wayback machine

This is my fav glitch/beat repeat (stutter) plugin
other good ones
- supatrigga
- livecut
- dblue glitch

8gs of samples from the dogs on acid forums on archive

I broke wine again somehow (raspberry pi4)

I broke wine on my raspberry pi
but switched harddrives and found that hangover actually ran fl v 10 better than v 20. it could run plugins and the gui wasnt all screwed up like before. had the opposite problem before where 20 ran better than 10 but the big trade off was it would freeze if i tried to run anything but stock plugins. part of the fix was using x instead of wayland and switching audio to pulse and installing the asio drivers that come with fl v 20. but im not entirely sure because when i tried to duplicate the results on a separate hard drive audio was glitchy again. I used cadence to bridge pulseaudio and alsa to jack but im not sure entirely if thats what fixed it. I will retry with fl 24.1 the october archived link from the imageline forum because 24.2 on mint wasnt working. 24.1 was good

  • arandr has been renamed
  • its now called raindrop

Monday, February 17, 2025

getting MPL align takes to work on a raspberry pi 4

I was getting an openGL error when trying to run some plugins in reaper on a raspberry pi. This line gets it working


# fix reaper opengl problems for mpl align takes to work
MESA_GL_VERSION_OVERRIDE=3.1COMPAT reaper

Sunday, February 16, 2025

slice on grid and randomize - Edison scripts for FL

Audio Scripts

put them in /Documents/Image-Line/FL-Studio/Settings/Audio scripts

Wednesday, February 12, 2025

F Major Uke Instrument - Decent Sampler

a picture of the plugin UI F Uke 160

My red plastic beaver creek ukulele playing the diatonic chords of F major across the keys with a strum pattern that loops at 160bpm.


some other included chords
E = eb
B = Bb minor
Ab = A7
Gb = G7
D# = D7
C# = C7

Tuesday, February 11, 2025

Friday, February 7, 2025

This Tool Lets You Convert Between Many Different Formats - Convert With Moss

convert with moss

Also It turns out renoise CAN route audio to the send effects from inputs.
I was trying to do it from the sampling tab. the proper way is to use the line in module

Thursday, February 6, 2025

anth-machine

my first Decent sampler Instrument


anth-machine

how to control mixxx using a usb gamepad

antimicrox snes dj mapping

DJing with a snes gamepad
The L + R buttons
- L activates layer 2 while held, (its a shift button)
- R moves the crossfader to the right
- L + R together moves it to the left
start and select = load deck 1 and 2
the D pad
Left and right = nudges track 1 forward/back
up and down = navigate file browser up/down
The Buttons
A B are play/pause for deck 1 and 2
X Y are hotcue 1 for both decks
use the L shift button with X Y to delete those hotcues
Shift and A B are the syncs for deck 1 and 2

Sunday, February 2, 2025

Figured out how to properly route audio with jack

Using mixxx I saw you could run LV2 plugins but most of them were grayed out
I installed vmpk (almost didn't because I saw it came with jackd and qjackctl, Jack was giving me trouble before)
Then I installed carla
It lets you manage all your plugins even the yabridge wine ones.
in qjackctl settings I set the midi driver to seq
I was scared of jack before because it made my audio stop working and it was annoying having to run qjack ctl and wire everything up just to get basic audio.
The 2 major benefits, low latency, and multiple connections at once.
Unfortunately Renoise line in recording doesn't seem to give you access to sends.
I managed to get reaper working though with audiothing's outerspace and orilreverb on send tracks
controlled using my acorn midi keyboards knobs
I was learning how to use loops in mixxx. I didnt need to midi learn them to a controller because
They are already on the stock qwerty keyboard
Im trying to make a habit of looping in tracks instead of drop mixxing everything.
On the Hercules Djay Control Compact I remapped the gain knobs to control filters instead, First I had them controlling the delay sends, but then I mapped them to the nanokontrol2 instead.
It Opens up a lot of possibilities being able to integrate those 32 bit windows vsts in wine and native 64bit linux plugins
Its so cool having an individual track for each effects output instead of just a stereo mix
qjackctl, carla, mixxx, reaper.
all open on seperate tabs in i3wm. The main thing is having everything midi mapped and saved as a template

getting fl studio working with jack instead of pulse

install catia it handles bridging between pulse and jack no need for using qjackctl you can have it run on login. Finally fixes the problem where I try to have youtube in the background and it wouldn't audio while FL was open.