-
Notifications
You must be signed in to change notification settings - Fork 8
/
tools.py
executable file
·208 lines (171 loc) · 5.84 KB
/
tools.py
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/usr/bin/env python3
from timecode import Timecode
def bitstring_to_bytes(s, bytecount=1, byteorder='big'):
return int(s, 2).to_bytes(bytecount, byteorder)
# binary big-endian
def bbe(n, bits=8):
# terminal condition
retval = ''
if n == 0:
retval = '0'
else:
retval = bbe(n//2, None) + str(n % 2)
if bits is None:
return retval
else:
return (('0'*bits) + retval)[-bits:]
# binary, little-endian
def ble(n, bits=8):
# terminal condition
retval = ''
if n == 0:
retval = '0'
else:
retval = str(n % 2) + ble(n//2, None)
if bits is None:
return retval
else:
return (retval + ('0'*bits))[0:bits]
def cint(n, bytecount=2):
return int(n).to_bytes(bytecount, byteorder='little')
def units_tens(n):
return n % 10, int(n/10)
##
# LTC functions
##
# GENERATE BINARY-CODED DATA FOR LTC
# ACCORDING TO https://en.wikipedia.org/wiki/Linear_timecode
# everything is encoded little endian
# so to encode the number 3 with four bits, we have 1100
def ltc_encode(timecode, as_string=False):
LTC = ''
HLP = ''
hrs, mins, secs, frs = timecode.frames_to_tc(timecode.frames)
frame_units, frame_tens = units_tens(frs)
secs_units, secs_tens = units_tens(secs)
mins_units, mins_tens = units_tens(mins)
hrs_units, hrs_tens = units_tens(hrs)
# frames units / user bits field 1 / frames tens
LTC += ble(frame_units, 4) + '0000' + ble(frame_tens, 2)
HLP += '---{u}____-{t}'.format(u=frame_units, t=frame_tens)
# drop frame / color frame / user bits field 2
LTC += '00'+'0000'
HLP += '__'+'____'
# secs units / user bits field 3 / secs tens
LTC += ble(secs_units, 4) + '0000' + ble(secs_tens, 3)
HLP += '---{u}____--{t}'.format(u=secs_units, t=secs_tens)
# bit 27 flag / user bits field 4
LTC += '0' + '0000'
HLP += '_' + '____'
# mins units / user bits field 5 / mins tens
LTC += ble(mins_units, 4) + '0000' + ble(mins_tens, 3)
HLP += '---{u}____--{t}'.format(u=mins_units, t=mins_tens)
# bit 43 flag / user bits field 6
LTC += '0' + '0000'
HLP += '_' + '____'
# hrs units / user bits field 7 / hrs tens
LTC += ble(hrs_units, 4) + '0000' + ble(hrs_tens, 2)
HLP += '---{u}____--{t}'.format(u=hrs_units, t=hrs_tens)
# bit 58 clock flag / bit 59 flag / user bits field 8
LTC += '0' + '0' + '0000'
HLP += '_' + '_' + '____'
# sync word
LTC += '0011111111111101'
HLP += '################'
if as_string:
return LTC
else:
return bitstring_to_bytes(LTC, bytecount=10)
##
# MTC functions
##
def mtc_encode(timecode, as_string=False):
# MIDI bytes are little-endian
# Byte 0
# 0rrhhhhh: Rate (0–3) and hour (0–23).
# rr = 000: 24 frames/s
# rr = 001: 25 frames/s
# rr = 010: 29.97 frames/s (SMPTE drop-frame timecode)
# rr = 011: 30 frames/s
# Byte 1
# 00mmmmmm: Minute (0–59)
# Byte 2
# 00ssssss: Second (0–59)
# Byte 3
# 000fffff: Frame (0–29, or less at lower frame rates)
hrs, mins, secs, frs = timecode.frames_to_tc(timecode.frames)
framerate = timecode.framerate
rateflags = {
'24': 0,
'25': 1,
'29.97': 2,
'30': 3
}
rateflag = rateflags[framerate] * 32 # multiply by 32, because the rate flag starts at bit 6
# print('{:8} {:8} {:8} {:8}'.format(hrs, mins, secs, frs))
if as_string:
b0 = bbe(rateflag + hrs, 8)
b1 = bbe(mins)
b2 = bbe(secs)
b3 = bbe(frs)
# print('{:8} {:8} {:8} {:8}'.format(b0, b1, b2, b3))
return b0+b1+b2+b3
else:
b = bytearray([rateflag + hrs, mins, secs, frs])
# debug_string = ' 0x{:02} 0x{:02} 0x{:02} 0x{:02}'
# debug_array = [ord(b[0]), ord(b[1]), ord(b[2]), ord(b[3])]
# print(debug_string.format(debug_array))
return b
# convert a bytearray back to timecode
def mtc_decode(mtc_bytes):
rhh, mins, secs, frs = mtc_bytes
rateflag = rhh >> 5
hrs = rhh & 31
fps = ['24', '25', '29.97', '30'][rateflag]
total_frames = int(frs + float(fps) * (secs + mins * 60 + hrs * 60 * 60))
# total frames must always be an integer above zero
if total_frames < 1:
total_frames = 1
return Timecode(fps, frames=total_frames)
def mtc_full_frame(timecode):
# if sending this to a MIDI device, remember that MIDI is generally little endian
# but the full frame timecode bytes are big endian
mtc_bytes = mtc_encode(timecode)
# mtc full frame has a special header and ignores the rate flag
return bytearray([0xf0, 0x7f, 0x7f, 0x01, 0x01]) + mtc_bytes + bytearray([0xf7])
def mtc_decode_full_frame(full_frame_bytes):
mtc_bytes = full_frame_bytes[5:-1]
return mtc_decode(mtc_bytes)
def mtc_quarter_frame(timecode, piece=0):
# there are 8 different mtc_quarter frame pieces
# see https://en.wikipedia.org/wiki/MIDI_timecode
# and https://web.archive.org/web/20120212181214/http://home.roadrunner.com/~jgglatt/tech/mtc.htm
# these are little-endian bytes
# piece 0 : 0xF1 0000 ffff frame
mtc_bytes = mtc_encode(timecode)
this_byte = mtc_bytes[3 - piece//2] # the order of pieces is the reverse of the mtc_encode
if piece % 2 == 0:
# even pieces get the low nibble
nibble = this_byte & 15
else:
# odd pieces get the high nibble
nibble = this_byte >> 4
return bytearray([0xf1, piece * 16 + nibble])
def mtc_decode_quarter_frames(frame_pieces):
mtc_bytes = bytearray(4)
if len(frame_pieces) < 8:
return None
for piece in range(8):
mtc_index = 3 - piece//2 # quarter frame pieces are in reverse order of mtc_encode
this_frame = frame_pieces[piece]
if this_frame is bytearray or this_frame is list:
this_frame = this_frame[1]
data = this_frame & 15 # ignore the frame_piece marker bits
if piece % 2 == 0:
# 'even' pieces came from the low nibble
# and the first piece is 0, so it's even
mtc_bytes[mtc_index] += data
else:
# 'odd' pieces came from the high nibble
mtc_bytes[mtc_index] += data * 16
return mtc_decode(mtc_bytes)