|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- #!/usr/bin/python3
- import argparse, collections, sys
-
- # MiOPMdrv sound bank Paramer Ver2002.04.22
- # LFO: LFRQ AMD PMD WF NFRQ
- # @:[Num] [Name]
- # CH: PAN FL CON AMS PMS SLOT NE
- # [OPname]: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN
- OpNames = ["M1","C1","M2","C2"]
- OpIdx = [0,2,1,3]
-
- def Error(*args):
- print(sys.argv[0]+":", *args, file=sys.stderr)
- sys.exit(1)
-
-
- def PrintChannel(pan, fl, con, ams, pms, slot, ne):
- print("CH:", pan, fl, con, ams, pms, slot, ne)
-
- def PrintOp(op, ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen):
- print(OpNames[op] + ":", ar, d1r, d2r, rr, d1l, tl, ks, mul, dt1, dt2, amsen)
-
- class Operator:
- def __init__(self, num):
- self.num = num
- for n in "ar d1r d2r rr d1l tl ks mul dt1 dt2 amsen".split():
- setattr(self, n, 0)
-
- def setDT1Mul(self, b):
- self.dt1, self.mul = (b & 0b01110000) >> 4, (b & 0b00001111)
-
- def setKSAR(self, b):
- self.ks, self.ar = (b & 0b11000000) >> 6, (b & 0b00011111)
-
- def setAmsenD1R(self, b):
- self.amsen, self.d1r = (b & 0b10000000) >> 7, (b & 0b00011111)
-
- def setDT2D2R(self, b):
- self.dt2, self.d2r = (b & 0b11000000) >> 6, (b & 0b00011111)
-
- def setD1LRR(self, b):
- self.d1l, self.rr = (b & 0b11110000) >> 4, (b & 0b00001111)
-
- class Instrument:
- def __init__(self, num, name):
- self.num = num
- self.name = name
- self.slot = 120
- self.pan = 64
- self.ne = 0
- for n in "fl con ams pms".split():
- setattr(self, n, 0)
-
- self.ops = [Operator(i) for i in range(4)]
-
- def render(self):
- print("@:%d %s" % (self.num, self.name))
- print("LFO: 0 0 0 0 0")
- PrintChannel(self.pan, self.fl, self.con, self.ams, self.pms, self.slot, self.ne)
- for o in self.ops:
- PrintOp(o.num, o.ar, o.d1r, o.d2r, o.rr, o.d1l, o.tl, o.ks, o.mul, o.dt1, o.dt2, o.amsen)
-
- def setFlCon(self, b):
- self.fl, self.con = (b & 0b00111000) >> 3, (b & 0b00000111)
-
- def setAMSPMS(self, b):
- self.ams, self.pms = (b & 0b00110000) >> 4, (b & 0b00000111)
- # """
- def __hash__(self):
- get = lambda n, d = 1: [getattr(self.ops[x], n) // d for x in range(4)]
- data = bytes([self.con] + get("tl", 4))
- #print(data, data.__hash__())
- return data.__hash__()
-
- def __eq__(self, other):
- return self.__hash__() == other.__hash__()
- # """
- class RecordGrabber:
- def __init__(self, fields, recordSize, **options):
- self.fields = fields
- self.recordSize = recordSize
- self.instStep = 1
- for k, v in options:
- setattr(self, k, v)
-
- def __call__(self,data):
- result = []
- f = self.fields
- for n in range(0, len(data) // self.recordSize):
- inst = Instrument(n, ("Ins%d" % n) if "name" not in f else
- f["name"](data, ofs))
-
- ofs = n * self.recordSize
-
- get = lambda s: data[ofs + f[s]] if s in f else 0
-
- inst.setFlCon(get("flcon"))
- inst.setAMSPMS(get("amspms"))
-
- for o in range(4):
- getOp = lambda s: data[ofs + f[s] + OpIdx[o]] if s in f else 0
- op = inst.ops[o]
-
- op.setDT1Mul(getOp("dt1mul"))
- op.tl = getOp("tl")
- op.setKSAR(getOp("ksar"))
- op.setAmsenD1R(getOp("amsend1r"))
- op.setDT2D2R(getOp("dt2d2r"))
- op.setD1LRR(getOp("d1lrr"))
-
- #inst.render()
- result.append(inst)
- #print(inst.__hash__())
- return result
-
-
- def PrintOPM(data):
- result = []
- for i in range(8):
- inst = Instrument(i, ("Ins%d" % i))
- inst.ne = (data[0xf] & 0b10000000) >> 7
-
- meta = data[0x20 + i : 0x3f + i : 8]
-
- inst.setFlCon(meta[0])
- inst.setAMSPMS(meta[3])
-
- for o in range(4):
- op = inst.ops[o]
- get = lambda x: data[x + i + OpIdx[o]*8]
-
- op.setDT1Mul(get(0x40))
- op.tl = get(0x60)
- op.setKSAR(get(0x80))
- op.setAmsenD1R(get(0xa0))
- op.setDT2D2R(get(0xc0))
- op.setD1LRR(get(0xe0))
- return result
-
- def PrintOPNBasic(data, opna = False):
- result = []
- # SR in opn parlance is equivalent to D2R in opm, thanks yamaha
- # likewise, SL -> D1L and DR -> D1R
- ofs = 0x100 if opna else 0
- for i in range(3):
- n = i + (3 if opna else 0)
- inst = Instrument(n, ("Ins%d" % n))
-
- inst.setFlCon(data[ofs + 0xb0 + i])
- inst.setAMSPMS(data[ofs + 0xb4 + i])
-
- for o in range(4):
- op = inst.ops[o]
- get = lambda x: data[ofs + x + i + OpIdx[o]*4]
-
- op.getDT1Mul(get(0x30))
- op.tl = get(0x40)
- op.getKSAR(get(0x50))
- op.getAmsenD1R(get(0x60))
- op.getDT2D2R(get(0x70))
- op.getD1LRR(get(0x80))
-
- result.append(inst)
- return result
-
- def PrintOPN(data):
- PrintOPNBasic(data)
-
- def PrintOPNA(data):
- PrintOPNBasic(data)
- PrintOPNBasic(data, True)
-
- TypeFuncs = {
- "opn": PrintOPN,
- "opna": PrintOPNA,
- "opm": PrintOPM,
- "raw": RecordGrabber({
- "flcon": 0x18,
- "amspms": 0x19,
- "dt1mul": 0x0,
- "tl": 0x04,
- "ksar": 0x08,
- "amsend1r": 0x0c,
- "dt2d2r": 0x10,
- "d1lrr": 0x14
- }, 0x20),
- "solfeace": RecordGrabber({
- "flcon": 0x00,
- "dt1mul": 0x02,
- "tl": 0x06,
- "ksar": 0x0a,
- "amsend1r": 0x0e,
- "dt2d2r": 0x12,
- "d1lrr": 0x16,
- "name": lambda data, ofs: data[ofs + 0x1a : ofs + 0x20].decode("shift-jis")
- }, 0x20)
- }
-
- Parser = argparse.ArgumentParser(
- prog="hootvopm",
- description="Convert Yamaha OPM/OPN data to VOPM instruments")
-
- Parser.add_argument("file", help="file to read (- for stdin)")
- Parser.add_argument("-b", "--binary", dest="binary", action="store_true",
- help="read binary output instead of comma-separated hex")
- Parser.add_argument("-t", "--type", dest="type", action="store",
- default="raw", choices=TypeFuncs.keys(),
- help="chip/format type (default: raw)")
-
- Args = Parser.parse_args()
-
- Filename = Args.file
-
- DataFile = open(Filename,"r" + ("b" if Args.binary else "")) if Filename != "-" else sys.stdin
- Data = [] if not Args.binary else DataFile.read()
-
- if not Args.binary:
- for line in DataFile.readlines():
- for byte in line.strip().split(",")[:16]:
- Data.append(int(byte,16))
-
-
- print("// Exported by hootvopm : https://git.lain.church/whut/hootvopm")
- print("// Chip type:", Args.type)
-
- Instruments = set(TypeFuncs[Args.type.lower()](Data))
- #print(len(Instruments))
- Instruments = sorted(list(Instruments), key = lambda x: x.num)
-
- count = 0
- for i in Instruments:
- i.num = count
- i.render()
- count += 1
|