diff options
-rw-r--r-- | README.md | 25 | ||||
-rwxr-xr-x | tlo-midi.py | 112 |
2 files changed, 137 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..718ad08 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +Turris light organ +================== +Simple script to blink with Turris leds to music. There is for now only +implementation using midi, but second implementation using wave and FFT is +planned. + +midi +---- +Requirements: + +* Python3 +* Python3-pip +* mido (Can be installed using pip) + +``` +opkg update +opkg install python3-pip +pip3 install mido +``` + +Now you should be able to run `tlo-midi.py` script. Input must be midi file. If +your midi file has more than one track, you might want to choose different one +using `-c` option. + +Tested with following midi file: https://www.youtube.com/watch?v=6zCzeTUOBKg diff --git a/tlo-midi.py b/tlo-midi.py new file mode 100755 index 0000000..77263c0 --- /dev/null +++ b/tlo-midi.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# This is simple script playing midi track on turris leds +# Currently tested only with turris omnia + +import os +import time +import argparse +import mido + +# Parse arguments +parser = argparse.ArgumentParser(description='Turris leds midi track player') +parser.add_argument('-c', nargs=1, type=int, help="Choose channel") +parser.add_argument('-d', nargs=1, type=str, help="Color in format 0xFFF") +parser.add_argument('-t', action='store_true', + help="If it is test run (print output to console)") +parser.add_argument('FILE', nargs=1, type=str, help="Midi audio file") +args = parser.parse_args() + +midi_file = args.FILE[0] +channel = 0 +if args.c is not None: + channel = args.c[0] +test_run = False +if args.t is not None: + test_run = args.t +color = '0xF00' # In default use red +if args.d is not None: + color = args.d[0] + +# Load midi file +midi = mido.MidiFile(midi_file) + +# Initialize data line (we have 12 leds) +data_line = [0]*12 +# Found out cut off values (maximal and minimal note in input midi file to use +# all leds to their fullest) +cut_bottom = None +cut_top = None +for msg in midi: + if (msg.type == "note_on" or msg.type == "note_off") and \ + msg.channel == channel: + if cut_bottom is None or msg.note < cut_bottom: + cut_bottom = msg.note + if cut_top is None or msg.note > cut_top: + cut_top = msg.note +if cut_bottom is None or cut_top is None: + raise Exception("Midi file seems to contain no audio data") + +rainbow_led = ( + 'pwr', + 'lan0', + 'lan1', + 'lan2', + 'lan3', + 'lan4', + 'wan', + 'pci1', + 'pci2', + 'pci3', + 'usr1', + 'usr2' + ) + + +def output_line(): + "Function used for pushing data out" + if test_run: + line = "" + for i in range(0, 12): + # We divide velocity by 13 to ensure resolution of 0-9 + # (velocity is 1..128) + line = line + str(int(data_line[i]/13)) + print(line) + else: + cmd = "rainbow " + for i in range(0, 12): + cmd = cmd + rainbow_led[i] + " " + color + " intensity " + # Here range is from 0-100 + cmd = cmd + str(int(data_line[i]/1.28)) + " " + os.system(cmd) + + +def note_to_line(note): + """ This function calculates which led is for given note + Note is number from 0..255 + """ + note = note - cut_bottom + # Threshold between notes + threshold = (cut_top - cut_bottom) / 12 + # Now divide note by threshold and shift it by 1 down for indexing + return int(note / threshold - 1) + + +def note(note, velocity, time_suspend, off=False): + if off: + velocity = 0 + time.sleep(time_suspend) # Suspend for given delay + data_line[note_to_line(note)] = velocity + + +# Now do the work +for msg in midi: + if (msg.type == "note_on" or msg.type == "note_off") and \ + msg.channel == channel: + note(msg.note, msg.velocity, msg.time, msg.type == "note_off") + else: + print("Ignoring message: " + str(msg)) + output_line() + +# Note: We are not handling beats at all. This just waits for given time when it's +# told to do that, but nothing special is done here with timing. This might be +# enough for basic midi files, but problematic for slow and fast beats. |