Dup Ver Goto 📝

SimpleScaleMapper

PT2/music/python does not exist
To
273 lines, 688 words, 5569 chars Page 'SimpleScaleMapper' does not exist.

A very quick and dirty script to map the white keys to a chosen major/minor scale. I've not tried out out with a real keyboard yet, just tested by sending a few note messages into it.

mapper

#!/usr/bin/env python
from icecream import ic
from rtmidi import MidiIn, MidiOut
import time

whitel = [0,2,4,5,7,9,11]
whites = set(whitel)
whited = { x:i for i,x in enumerate(whitel) }
major = [0,2,4,5,7,9,11]
minor = [0,2,3,5,7,8,10]

class Mapper:
  def __init__(self,inport,outport):
    self.midiin = MidiIn()
    self.midiout = MidiOut()
    self.root = 0
    self.select = None
    self.scale = major
    self.isminor = False
    self.midiin.set_callback(self.midi_callback)
    self.midiout.open_port(outport)
    self.midiin.open_port(inport)
  def close(self):
    self.midiin.close_port()
    self.midiout.close_port()
  def midi_callback(self,x,y):
    msg,dt = x
    status = msg[0] & 0xF0
    ch = status & 0x0F
    match status:
      case 0x80:
        p = msg[1]
        return self.key(p,0,ch)
      case 0x90:
        p,v = msg[1:3]
        return self.key(p,v,ch)
      case _:
        return passthru(msg)
  def passthru(self,msg):
    self.midiout.send_message(msg)
  def key(self,p,v=100,ch=0):
    if p % 12 in whites:
      self.whitekey(p,v)
    else:
      self.blackkey(p,v)
  def blackkey(self,p,v,ch=0):
    p %= 12
    match p:
      case 1: # C#
        ic("select major")
        self.select = "major"
        self.scale = major
        self.isminor = False
      case 3:
        ic("select minor")
        self.select = "minor"
        self.scale = minor
        self.isminor = True
      case _:
        self.select = None
  def whitekey(self,p,v,ch=0):
    a = p % 12
    o = p // 12
    match self.select:
      case "major":
        self.root = a
        self.select = None
      case "minor":
        self.root = (a + 3) % 12
        self.select = None
      case None:
        return self.mapwhitekey(p,v)
  # turn note offs into note ons with velocity 0 and discard note off velocity
  def mapwhitekey(self,p,v,ch=0):
    #ic(p,v,self.root)
    a = p % 12
    o = p // 12
    b = whited[a]
    c = self.root + self.scale[b]
    if self.isminor:
      c -= 3
    c += 12*o
    self.output(c,v,ch)
  def output(self,p,v,ch):
    status = (ch & 0x0F) | 0x90
    msg = bytes((status,p,v))
    ic(p,v)
    self.midiout.send_message(msg)

def findin(pat):
  pat = pat.lower()
  m = MidiIn()
  xs = []
  for i,x in enumerate(m.get_ports()):
    if pat in x.lower():
      print(f"Found {i}: {x}")
      xs.append(i)
  return xs
def findout(pat):
  pat = pat.lower()
  m = MidiOut()
  xs = []
  for i,x in enumerate(m.get_ports()):
    if pat in x.lower():
      print(f"Found {i}: {x}")
      xs.append(i)
  return xs

import sys
args = sys.argv[1:]
if len(args) == 0:
  m = MidiIn()
  print("Inputs")
  for i,x in enumerate(m.get_ports()):
    print(i,x)
  print("Outputs")
  m = MidiOut()
  for i,x in enumerate(m.get_ports()):
    print(i,x)
  exit(0)

try:
  inpat,outpat = args[:2]
except Exception:
  print(f"{sys.argv[0]}     # with no arguments lists midi ports")
  print(f"{sys.argv[0]} <in pattern> <out pattern>  # runs the mapper")
  exit(1)

ins = findin(inpat)
outs = findout(outpat)

if len(ins) == 0:
  print(f"Found no input")
  exit(2)
if len(outs) == 0:
  print(f"Found no output")
  exit(3)

m = Mapper(ins[0],outs[0])
try:
  while True:
    time.sleep(1)
except KeyboardInterrupt:
  print(f"Ctrl-C")
  m.close()
  exit(0)

recv

#!/usr/bin/env python
from icecream import ic
from rtmidi import MidiIn, MidiOut
import time

def midi_callback(x,y):
  msg,dt = x
  ic(msg)

def findin(pat):
  pat = pat.lower()
  m = MidiIn()
  xs = []
  for i,x in enumerate(m.get_ports()):
    if pat in x.lower():
      print(f"Found {i}: {x}")
      xs.append(i)
  return xs

import sys
args = sys.argv[1:]
if len(args) == 0:
  m = MidiIn()
  print("Inputs")
  for i,x in enumerate(m.get_ports()):
    print(i,x)
  exit(0)

try:
  inpat = args[0]
except Exception:
  print(f"{sys.argv[0]}     # with no arguments lists midi ports")
  print(f"{sys.argv[0]} <in pattern>  # runs the mapper")
  exit(1)

ins = findin(inpat)

if len(ins) == 0:
  print(f"Found no input")
  exit(2)

i = ins[0]
m = MidiIn()
m.set_callback(midi_callback)
m.open_port(i)
try:
  while True:
    time.sleep(1)
except KeyboardInterrupt:
  print(f"Ctrl-C")
  m.close_port()
  exit(0)

send

#!/usr/bin/env python
from icecream import ic
from rtmidi import MidiIn, MidiOut
import time

def findin(pat):
  pat = pat.lower()
  m = MidiIn()
  xs = []
  for i,x in enumerate(m.get_ports()):
    if pat in x.lower():
      print(f"Found {i}: {x}")
      xs.append(i)
  return xs
def findout(pat):
  pat = pat.lower()
  m = MidiOut()
  xs = []
  for i,x in enumerate(m.get_ports()):
    if pat in x.lower():
      print(f"Found {i}: {x}")
      xs.append(i)
  return xs

import sys
args = sys.argv[1:]
if len(args) == 0:
  print("Outputs")
  m = MidiOut()
  for i,x in enumerate(m.get_ports()):
    print(i,x)
  exit(0)

try:
  outpat,*ps = args
  ps = map(int,ps)
except Exception:
  print(f"{sys.argv[0]}     # with no arguments lists midi ports")
  print(f"{sys.argv[0]} <out pattern> pitches # runs the mapper")
  exit(1)

outs = findout(outpat)

if len(outs) == 0:
  print(f"Found no output")
  exit(3)

o = outs[0]

m = MidiOut()
m.open_port(o)
for p in ps:
  ic("Sending",p)
  msg = bytes((0x90,p&127,100))
  m.send_message(msg)
  time.sleep(0.1)
  msg = bytes((0x90,p&127,0))
  m.send_message(msg)
  time.sleep(0.1)
m.close_port()