-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathitfile.py
141 lines (118 loc) · 4.24 KB
/
itfile.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
import sys, struct, random, math
from samples import *
# IT format constants. leave these alone.
IT_FLAG_STEREO = 0x01
IT_FLAG_VOL0MIX = 0x02 # absolutely useless since 1.04.
IT_FLAG_INSTR = 0x04
IT_FLAG_LINEAR = 0x08
IT_FLAG_OLDEFF = 0x10 # don't enable this, it's not well documented.
IT_FLAG_COMPATGXX = 0x20 # don't enable this, it's not well documented.
IT_FLAG_PWHEEL = 0x40 # MIDI-related, don't use
IT_FLAG_USEMIDI = 0x80 # undocumented MIDI crap, don't use
IT_SPECIAL_MESSAGE = 0x01 # MIDI-related, don't use
IT_SPECIAL_UNK1 = 0x02 # undocumented MIDI crap, don't use
IT_SPECIAL_UNK2 = 0x04 # undocumented MIDI crap, don't use
IT_SPECIAL_HASMIDI = 0x08 # undocumented MIDI crap, don't use
class ITFile:
def __init__(self, tempo, speed):
self.name = "autotracker-remastered module"
self.flags = IT_FLAG_STEREO
self.highlight = 0x1004
self.ordlist = []
self.inslist = []
self.smplist = []
self.patlist = []
self.chnpan = [32 for i in xrange(64)]
self.chnvol = [64 for i in xrange(64)]
self.version = 0x0217
self.vercompat = 0x0200
self.flags = (
IT_FLAG_STEREO
| IT_FLAG_VOL0MIX # in the exceptionally rare case it may help...
| IT_FLAG_LINEAR
)
self.special = (
IT_SPECIAL_MESSAGE
)
self.gvol = 128
self.mvol = 48
self.tempo = tempo
self.speed = speed # ticks per row, higher = slower
self.pitchwheel = 0
self.pansep = 128
self.message = (
"Generated with Autotracker-Remastered"
)
def enqueue_ptr(self, call):
self.ptrq.append((self.fp.tell(),call))
self.write("00PS")
def save(self, fname):
self.fp = open(fname,"wb")
self.message_fixed = self.message.replace("\n\r","\n").replace("\n","\r") + "\x00\x00"
self.ptrq = []
self.doheader(self)
while self.ptrq:
pos, f = self.ptrq.pop(0)
t = self.fp.tell()
self.fp.seek(pos)
self.write(struct.pack("<I",t))
self.fp.seek(t)
f(self)
self.fp.close()
def w_msg(self, fp):
fp.write(self.message_fixed)
def doheader(self, fp):
ordlist = self.ordlist[:]
if (not ordlist) or ordlist[-1] != 0xFF:
ordlist.append(0xFF)
fp.write("IMPM")
fp.write_padded(25, self.name)
fp.write(struct.pack("<HHHHHHHHH"
,self.highlight
,len(ordlist),len(self.inslist),len(self.smplist),len(self.patlist)
,self.version,self.vercompat,self.flags,self.special
))
fp.write(struct.pack("<BBBBBBH"
,self.gvol,self.mvol,self.speed,self.tempo
,self.pansep,self.pitchwheel,len(self.message_fixed)
))
fp.enqueue_ptr(self.w_msg)
fp.write("AtBu")
fp.write(''.join(chr(v) for v in self.chnpan))
fp.write(''.join(chr(v) for v in self.chnvol))
fp.write(''.join(chr(v) for v in ordlist))
l = self.inslist + self.smplist + self.patlist
for i in xrange(len(l)):
self.enqueue_ptr(l[i].write)
fp.write(struct.pack("<HI",0,0))
def write(self, data):
self.fp.write(data)
def write_padded(self, length, s, addnull = True):
if len(s) < length:
s += "\x00"*(length-len(s))
else:
s = s[:length]
assert len(s) == length, "STUPID! write_padded gave wrong length!"
self.write(s)
if addnull:
self.write("\x00")
def write_sample_array(self, data):
# TODO: allow for a compressor / proper saturator
if SMP_16BIT:
for v in data:
d = max(-0x8000,min(0x7FFF,int(v * 0x7FFF)))
self.write(struct.pack("<h", d))
else:
for v in data:
d = max(-0x80,min(0x7F,int(v * 0x7F)))
self.write(struct.pack("<b",d))
def smp_add(self, smp):
self.smplist.append(smp)
idx = len(self.smplist)
return idx
def pat_add(self, pat):
idx = len(self.patlist)
self.patlist.append(pat)
return idx
def ord_add(self, order):
self.ordlist.append(order)