SickGear/lib/hachoir_parser/audio/modplug.py
echel0n 0d9fbc1ad7 Welcome to our SickBeard-TVRage Edition ...
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer.

Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer.

Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk!

Enjoy!
2014-03-09 22:39:12 -07:00

291 lines
10 KiB
Python

"""
Modplug metadata inserted into module files.
Doc:
- http://modplug.svn.sourceforge.net/viewvc/modplug/trunk/modplug/soundlib/
Author: Christophe GISQUET <christophe.gisquet@free.fr>
Creation: 10th February 2007
"""
from lib.hachoir_core.field import (FieldSet,
UInt32, UInt16, UInt8, Int8, Float32,
RawBytes, String, GenericVector, ParserError)
from lib.hachoir_core.endian import LITTLE_ENDIAN
from lib.hachoir_core.text_handler import textHandler, hexadecimal
MAX_ENVPOINTS = 32
def parseComments(parser):
size = parser["block_size"].value
if size > 0:
yield String(parser, "comment", size)
class MidiOut(FieldSet):
static_size = 9*32*8
def createFields(self):
for name in ("start", "stop", "tick", "noteon", "noteoff",
"volume", "pan", "banksel", "program"):
yield String(self, name, 32, strip='\0')
class Command(FieldSet):
static_size = 32*8
def createFields(self):
start = self.absolute_address
size = self.stream.searchBytesLength("\0", False, start)
if size > 0:
self.info("Command: %s" % self.stream.readBytes(start, size))
yield String(self, "command", size, strip='\0')
yield RawBytes(self, "parameter", (self._size//8)-size)
class MidiSFXExt(FieldSet):
static_size = 16*32*8
def createFields(self):
for index in xrange(16):
yield Command(self, "command[]")
class MidiZXXExt(FieldSet):
static_size = 128*32*8
def createFields(self):
for index in xrange(128):
yield Command(self, "command[]")
def parseMidiConfig(parser):
yield MidiOut(parser, "midi_out")
yield MidiSFXExt(parser, "sfx_ext")
yield MidiZXXExt(parser, "zxx_ext")
def parseChannelSettings(parser):
size = parser["block_size"].value//4
if size > 0:
yield GenericVector(parser, "settings", size, UInt32, "mix_plugin")
def parseEQBands(parser):
size = parser["block_size"].value//4
if size > 0:
yield GenericVector(parser, "gains", size, UInt32, "band")
class SoundMixPluginInfo(FieldSet):
static_size = 128*8
def createFields(self):
yield textHandler(UInt32(self, "plugin_id1"), hexadecimal)
yield textHandler(UInt32(self, "plugin_id2"), hexadecimal)
yield UInt32(self, "input_routing")
yield UInt32(self, "output_routing")
yield GenericVector(self, "routing_info", 4, UInt32, "reserved")
yield String(self, "name", 32, strip='\0')
yield String(self, "dll_name", 64, desc="Original DLL name", strip='\0')
class ExtraData(FieldSet):
def __init__(self, parent, name, desc=None):
FieldSet.__init__(self, parent, name, desc)
self._size = (4+self["size"].value)*8
def createFields(self):
yield UInt32(self, "size")
size = self["size"].value
if size:
yield RawBytes(self, "data", size)
class XPlugData(FieldSet):
def __init__(self, parent, name, desc=None):
FieldSet.__init__(self, parent, name, desc)
self._size = (4+self["size"].value)*8
def createFields(self):
yield UInt32(self, "size")
while not self.eof:
yield UInt32(self, "marker")
if self["marker"].value == 'DWRT':
yield Float32(self, "dry_ratio")
elif self["marker"].value == 'PORG':
yield UInt32(self, "default_program")
def parsePlugin(parser):
yield SoundMixPluginInfo(parser, "info")
# Check if VST setchunk present
size = parser.stream.readBits(parser.absolute_address+parser.current_size, 32, LITTLE_ENDIAN)
if 0 < size < parser.current_size + parser._size:
yield ExtraData(parser, "extra_data")
# Check if XPlugData is present
size = parser.stream.readBits(parser.absolute_address+parser.current_size, 32, LITTLE_ENDIAN)
if 0 < size < parser.current_size + parser._size:
yield XPlugData(parser, "xplug_data")
# Format: "XXXX": (type, count, name)
EXTENSIONS = {
# WriteInstrumentHeaderStruct@Sndfile.cpp
"XTPM": {
"..Fd": (UInt32, 1, "Flags"),
"..OF": (UInt32, 1, "Fade out"),
"..VG": (UInt32, 1, "Global Volume"),
"...P": (UInt32, 1, "Panning"),
"..EV": (UInt32, 1, "Volume Envelope"),
"..EP": (UInt32, 1, "Panning Envelope"),
".EiP": (UInt32, 1, "Pitch Envelope"),
".SLV": (UInt8, 1, "Volume Loop Start"),
".ELV": (UInt8, 1, "Volume Loop End"),
".BSV": (UInt8, 1, "Volume Sustain Begin"),
".ESV": (UInt8, 1, "Volume Sustain End"),
".SLP": (UInt8, 1, "Panning Loop Start"),
".ELP": (UInt8, 1, "Panning Loop End"),
".BSP": (UInt8, 1, "Panning Substain Begin"),
".ESP": (UInt8, 1, "Padding Substain End"),
"SLiP": (UInt8, 1, "Pitch Loop Start"),
"ELiP": (UInt8, 1, "Pitch Loop End"),
"BSiP": (UInt8, 1, "Pitch Substain Begin"),
"ESiP": (UInt8, 1, "Pitch Substain End"),
".ANN": (UInt8, 1, "NNA"),
".TCD": (UInt8, 1, "DCT"),
".AND": (UInt8, 1, "DNA"),
"..SP": (UInt8, 1, "Panning Swing"),
"..SV": (UInt8, 1, "Volume Swing"),
".CFI": (UInt8, 1, "IFC"),
".RFI": (UInt8, 1, "IFR"),
"..BM": (UInt32, 1, "Midi Bank"),
"..PM": (UInt8, 1, "Midi Program"),
"..CM": (UInt8, 1, "Midi Channel"),
".KDM": (UInt8, 1, "Midi Drum Key"),
".SPP": (Int8, 1, "PPS"),
".CPP": (UInt8, 1, "PPC"),
".[PV": (UInt32, MAX_ENVPOINTS, "Volume Points"),
".[PP": (UInt32, MAX_ENVPOINTS, "Panning Points"),
"[PiP": (UInt32, MAX_ENVPOINTS, "Pitch Points"),
".[EV": (UInt8, MAX_ENVPOINTS, "Volume Enveloppe"),
".[EP": (UInt8, MAX_ENVPOINTS, "Panning Enveloppe"),
"[EiP": (UInt8, MAX_ENVPOINTS, "Pitch Enveloppe"),
".[MN": (UInt8, 128, "Note Mapping"),
"..[K": (UInt32, 128, "Keyboard"),
"..[n": (String, 32, "Name"),
".[nf": (String, 12, "Filename"),
".PiM": (UInt8, 1, "MixPlug"),
"..RV": (UInt16, 1, "Volume Ramping"),
"...R": (UInt16, 1, "Resampling"),
"..SC": (UInt8, 1, "Cut Swing"),
"..SR": (UInt8, 1, "Res Swing"),
"..MF": (UInt8, 1, "Filter Mode"),
},
# See after "CODE tag dictionary", same place, elements with [EXT]
"STPM": {
"...C": (UInt32, 1, "Channels"),
".VWC": (None, 0, "CreatedWith version"),
".VGD": (None, 0, "Default global volume"),
"..TD": (None, 0, "Default tempo"),
"HIBE": (None, 0, "Embedded instrument header"),
"VWSL": (None, 0, "LastSavedWith version"),
".MMP": (None, 0, "Plugin Mix mode"),
".BPR": (None, 0, "Rows per beat"),
".MPR": (None, 0, "Rows per measure"),
"@PES": (None, 0, "Chunk separator"),
".APS": (None, 0, "Song Pre-amplification"),
"..MT": (None, 0, "Tempo mode"),
"VTSV": (None, 0, "VSTi volume"),
}
}
class MPField(FieldSet):
def __init__(self, parent, name, ext, desc=None):
FieldSet.__init__(self, parent, name, desc)
self.ext = ext
self.info(self.createDescription())
self._size = (6+self["data_size"].value)*8
def createFields(self):
# Identify tag
code = self.stream.readBytes(self.absolute_address, 4)
if code in self.ext:
cls, count, comment = self.ext[code]
else:
cls, count, comment = RawBytes, 1, "Unknown tag"
# Header
yield String(self, "code", 4, comment)
yield UInt16(self, "data_size")
# Data
if not cls:
size = self["data_size"].value
if size > 0:
yield RawBytes(self, "data", size)
elif cls in (String, RawBytes):
yield cls(self, "value", count)
else:
if count > 1:
yield GenericVector(self, "values", count, cls, "item")
else:
yield cls(self, "value")
def createDescription(self):
return "Element '%s', size %i" % \
(self["code"]._description, self["data_size"].value)
def parseFields(parser):
# Determine field names
ext = EXTENSIONS[parser["block_type"].value]
if ext == None:
raise ParserError("Unknown parent '%s'" % parser["block_type"].value)
# Parse fields
addr = parser.absolute_address + parser.current_size
while not parser.eof and parser.stream.readBytes(addr, 4) in ext:
field = MPField(parser, "field[]", ext)
yield field
addr += field._size
# Abort on unknown codes
parser.info("End of extension '%s' when finding '%s'" %
(parser["block_type"].value, parser.stream.readBytes(addr, 4)))
class ModplugBlock(FieldSet):
BLOCK_INFO = {
"TEXT": ("comment", True, "Comment", parseComments),
"MIDI": ("midi_config", True, "Midi configuration", parseMidiConfig),
"XFHC": ("channel_settings", True, "Channel settings", parseChannelSettings),
"XTPM": ("instrument_ext", False, "Instrument extensions", parseFields),
"STPM": ("song_ext", False, "Song extensions", parseFields),
}
def __init__(self, parent, name, desc=None):
FieldSet.__init__(self, parent, name, desc)
self.parseBlock = parsePlugin
t = self["block_type"].value
self.has_size = False
if t in self.BLOCK_INFO:
self._name, self.has_size, desc, parseBlock = self.BLOCK_INFO[t]
if callable(desc):
self.createDescription = lambda: desc(self)
if parseBlock:
self.parseBlock = lambda: parseBlock(self)
if self.has_size:
self._size = 8*(self["block_size"].value + 8)
def createFields(self):
yield String(self, "block_type", 4)
if self.has_size:
yield UInt32(self, "block_size")
if self.parseBlock:
for field in self.parseBlock():
yield field
if self.has_size:
size = self["block_size"].value - (self.current_size//8)
if size > 0:
yield RawBytes(self, "data", size, "Unknown data")
def ParseModplugMetadata(parser):
while not parser.eof:
block = ModplugBlock(parser, "block[]")
yield block
if block["block_type"].value == "STPM":
break
# More undocumented stuff: date ?
size = (parser._size - parser.absolute_address - parser.current_size)//8
if size > 0:
yield RawBytes(parser, "info", size)