1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#!/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
import copy
from colour import Color
# 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 #FFFFFF")
parser.add_argument('-v', action='store_true', help="Print verbose messages")
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 = Color('#FF0000') # In default use red
if args.d is not None:
color = 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")
def calc_color(intens):
# intens is 0..127
multi = intens/127
clr = Color(color)
clr.red = clr.red * multi
clr.green = clr.green * multi
clr.blue = clr.blue * multi
return clr.get_hex_l()[1:]
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..127)
line = line + str(int(data_line[i]/13))
print(line)
else:
cmd = "rainbow "
for i in range(0, 12):
clr = calc_color(data_line[i])
cmd = cmd + rainbow_led[i] + " " + clr + " "
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, off=False):
if off:
velocity = 0
data_line[note_to_line(note)] = velocity
# Now do the work
work_time = int(time.time()*1000000)
for msg in midi:
if (msg.type == "note_on" or msg.type == "note_off") and \
msg.channel == channel:
note(msg.note, msg.velocity, msg.type == "note_off")
elif args.v:
print("Ignoring message: " + str(msg))
work_time = work_time + int(msg.time*1000000)
# Lets use busy delay to minimalize possibility of missing it
while work_time > int(time.time()*1000000):
pass
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.
|