Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python2
- '''
- Short script to make OPL music (in DOSBox DRO files) less loud.
- '''
- from struct import *
- from collections import namedtuple
- import sys
- import argparse
- def namedunpack(data, names, format):
- '''
- Unpacks into a named tuple.
- '''
- str = namedtuple('struct', names)
- data = str(*unpack(format, data))
- return data
- CARRIERS = (0x43, 0x44, 0x45, 0x4B, 0x4C, 0x4D, 0x53, 0x54, 0x55)
- LEVEL_TO_ALGORITHM = {
- 0x40: 0xC0, 0x41: 0xC0, 0x42: 0xC1, 0x43: 0xC1, 0x44: 0xC2, 0x45: 0xC2,
- 0x48: 0xC3, 0x49: 0xC3, 0x4A: 0xC4, 0x4B: 0xC4, 0x4C: 0xC5, 0x4D: 0xC5,
- 0x50: 0xC6, 0x51: 0xC6, 0x52: 0xC7, 0x53: 0xC7, 0x54: 0xC8, 0x55: 0xC8
- }
- class OPL2Quieter(object):
- def __init__(self, quiet_function):
- self.quiet_function = quiet_function
- self.registers = [0x00 for i in xrange(0x100)]
- def write(self, register, value):
- if 0x40 <= register <= 0x55: # if it is a level register
- # algorithm bit is the LSB of algorithm/feedback reg
- algorithm = self.registers[LEVEL_TO_ALGORITHM[register]] & 0x01
- if algorithm == 0x00:
- # FM synthesis, only modify the level if it's a carrier
- modify_level = True if register in CARRIERS else False
- else:
- # AM synthesis, modify level for both operators
- modify_level = True
- if modify_level:
- # keep the top two bits (the KSL value) separate
- # for purposes of calculation
- ksl = value & 0xC0
- # perform the volume modification on the lower six bits
- level = self.quiet_function(value & 0x3F)
- value = ksl | (level & 0x3F)
- self.registers[register] = value
- return register, value
- parser = argparse.ArgumentParser(
- description='Reduces the volume of a DOSBox DRO file.'
- )
- parser.add_argument('infile', help='input file', type=str)
- parser.add_argument('outfile', help='output file', type=str)
- args = parser.parse_args()
- infile = open(args.infile, 'rb')
- outfile = open(args.outfile, 'wb')
- chunk = infile.read(26)
- data = namedunpack(chunk, ['cSignature', 'iVersionMajor', 'iVersionMinor', 'iLengthPairs', 'iLengthMS', 'iHardwareType', 'iFormat', 'iCompression', 'iShortDelayCode', 'iLongDelayCode', 'iCodemapLength'], '8sHHIIBBBBBB')
- outfile.write(chunk)
- if data.cSignature != 'DBRAWOPL':
- parser.error('input file is not a DOSBox DRO file.')
- sys.exit(1)
- # Copy over the codemap table, which has a variable length.
- chunk = infile.read(data.iCodemapLength)
- outfile.write(chunk)
- pairsTotal = data.iLengthPairs
- pairsRead = 0
- quieter = OPL2Quieter(lambda level: max(level - 5, 0))
- while pairsRead < pairsTotal:
- # Read the next OPL bytes from the file
- chunk = infile.read(2)
- pairsRead += 1
- if not chunk: break
- reg, val = unpack('BB', chunk)
- reg, val = quieter.write(reg, val)
- outfile.write(pack('BB', reg, val))
- infile.close()
- outfile.close()
Add Comment
Please, Sign In to add comment