From dcaa716d29948b1f6eb126f4e80fdc47a041a700 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 23 Sep 2021 04:03:08 +0200 Subject: [PATCH] midicontrol example --- README.md | 7 +- libs/alink.py | 9 ++- libs/midix.py | 2 +- midicontrol.py | 202 +++++++++++++++++++++++++++++++++++++++++++++++++ miredis.py | 1 - 5 files changed, 214 insertions(+), 7 deletions(-) create mode 100644 midicontrol.py diff --git a/README.md b/README.md index a9d21ad..4be92c0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Miredis hooks to all midi devices connected and listen for events. Miredis can optionnaly hook to opensourced Link protocol (200+ music & videos apps) -> "/beat" & "/bpm" -All events are forwarded to a redis server and an OSC server. +All events are forwarded to your redis server and your OSC server. ![Clitools](https://www.teamlaser.fr/images/miredispad.png) @@ -19,8 +19,11 @@ To enable Link : python3 miredis.py -link +(for cheap midi interface midisport/midiman from audio on Linux : apt-get install midisport-firmware) + ## New Features +- Client example : midicontrol.py - Added verbose mode -v - Added redis subscribe events - Added Clitools program selection mode for Launchpads @@ -29,6 +32,8 @@ python3 miredis.py -link ## OSC +Following messages will be sent to an outside OSC server : + /midi/noteon midichannel note velocity /midi/noteoff midichannel note diff --git a/libs/alink.py b/libs/alink.py index b626d47..21b2bde 100644 --- a/libs/alink.py +++ b/libs/alink.py @@ -4,17 +4,18 @@ ''' -Ableton Link with bridge to OSC & Redis +alink v0.2 +Ableton Link bridge to OSC & Redis LICENCE : CC Sam Neurohack -Get: +Needs link.cpython library. Comes with some precompiled libraries. + +To build it : git clone --recursive https://github.com/gonzaloflirt/link-python.git -Build: - Make sure python 3 is installed on your system. mkdir build diff --git a/libs/midix.py b/libs/midix.py index 8db7400..bcbbfd1 100644 --- a/libs/midix.py +++ b/libs/midix.py @@ -162,7 +162,7 @@ def SendUI(oscaddress,oscargs=''): osclientlj.sendto(oscmsg, (TouchOSCIP, TouchOSCPort)) oscmsg.clearData() except: - log.err('Connection to Aurora UI refused : died ?') + log.err('Connection to UI refused : died ?') pass #time.sleep(0.001 diff --git a/midicontrol.py b/midicontrol.py new file mode 100644 index 0000000..41abaad --- /dev/null +++ b/midicontrol.py @@ -0,0 +1,202 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + + +''' + +Midi controlled skeleton +v0.1.0 + +Needs miredis (listen to midi devices) + + +Licensed under GNU GPLv3 + +by Sam Neurohack + +''' + +import sys +import traceback +import argparse +import redis +import ast + +argsparser = argparse.ArgumentParser(description="Midi/Redis control skeleton") +argsparser.add_argument("-c","--channel",help="Default midi channel (1)",default="1",type=str) +argsparser.add_argument("-m","--mode",help="Default running mode (33)",default="33",type=str) +args = argsparser.parse_args() + +cchannel=args.channel +note = args.mode + + +ip = '127.0.0.1' +port = 6379 +rds = redis.Redis( + host=ip, + port=port) + +MidiSub = rds.pubsub() +MidiSub.subscribe("/midi/last_event") +print("Midi events subscribed") + +cc = [0]*128 +bpm = 120 +notejump = {"33": "modeA()", + "35": "modeB()", + "44": "sys.exit(0)" # Beware + } + + +def MidiSubEvent(): + global note + + msg = MidiSub.get_message() + + if msg: + message = str(msg['data']) + + # Noteon change main function via notejump + if message.find('noteon') != -1: + noteon = message.split("/") + #print(noteon) + #if noteon[3] == cchannel: + + if noteon[4] in notejump: + note = noteon[4] + print(notejump[note]) + + +# Audio Redis keys continuously updated by specredis +def Current(): + global data1, data2, data3 + + # live audio parameters from specredis.py in redis + + # if data1 key like "[2.96, 1.01, 4.89, 3.29, 2.53, 2.10, 1.84, 1.66, 1.56, 1.50]" + data1redis = rds.get('levels') + data1 = ast.literal_eval(data1redis.decode('ascii')) + data1width = len(data1) + data2 = float(rds.get('spectrummax')) + data3 = float(rds.get('spectrumsum')) + + +# 1 CC -> 7 bits number : 0-127 +# rerange end value between low high +# midifactor(ccnumber, channel, low, high, default) +def midifactor(ccnumber, channel = cchannel, low =0, high=127, default =1): + + ccvalue = rds.get('/midi/cc/'+str(channel)+'/'+str(ccnumber)) + + if ccvalue is not None: + return rerange(int(ccvalue), 0, 127, low, high) + + else: + print('Default value returned. No midi value in redis for channel', channel, 'CC', ccnumber) + return default + +# 2 CC -> 14 bits number : 0 - 16383 +# rerange end value between low high +# midifactor(ccnumber, channel, low, high, default) +def midifactor14(highcc, lowcc, channel = cchannel, low =0, high=16383, default =1): + + lowvalue = rds.get('/midi/cc/'+str(channel)+'/'+str(lowcc)) + highvalue = rds.get('/midi/cc/'+str(channel)+'/'+str(highcc)) + + if lowvalue is not None and highcc is not None: + return rerange((int(highvalue) << 7)+ int(lowvalue), 0, 16383, low, high) + + else: + print('Default value returned. No midi value in redis for channel', channel, 'CC', ccnumber) + return default + + +# increase or decrease value +def Encoder(ccnumber, channel = cchannel): + + ccvalue = rds.get('/midi/cc/'+str(channel)+'/'+str(ccnumber)) + + # encoder turned to right + if ccvalue == 1: + EncoderPlusOne(ccnumber) + + # encoder turned to left + if ccvalue == 127: + EncoderMinusOne(ccnumber) + +def EncoderPlusOne(ccnumber): + global cc + + cc[ccnumber] +=1 + print(cc[ccnumber]) + +def EncoderMinusOne(ccnumber): + global cc + + if cc[ccnumber] >0: + cc[ccnumber] -= 1 + print(cc[ccnumber]) + +def rerange(s,a1,a2,b1,b2): + return b1 + ((s - a1) * (b2 - b1) / (a2 - a1)) + +# +# mode A +# + + +def modeA(): + + print(midifactor(0, channel = cchannel, low =0, high=127, default =1)) + pass + + +# +# mode B +# + +def modeB(): + + print(midifactor14(0, 1, channel = cchannel, low =0, high=16383, default =1)) + pass + + +# +# Main loop +# + +if __name__ == "__main__": + + note = "33" + running = True + + try: + + if (note in notejump) == False: + print('No function for that note') + sys.exit(0) + print('running', notejump[note]) + + while running: + + # Some redis keys pull + Current() + + # /noteon redis subscribe event like noteon -> change mode via eval + MidiSubEvent() + + eval(notejump[str(note)]) + + + except Exception: + print(traceback.print_exc()) + + except KeyboardInterrupt: + sys.exit(0) + + finally: + print("End") + sys.exit(0) + diff --git a/miredis.py b/miredis.py index ca2206e..805d63f 100644 --- a/miredis.py +++ b/miredis.py @@ -106,7 +106,6 @@ if __name__ == '__main__': midix.check() midix.loadConf() - midix.toKey("/beats","0.0") midix.toKey("/bpm",120)