Merge commit '57e889fb45a31abda48930fecf8006ef1db4ffcc' as 'python/fsb5'
This commit is contained in:
131
python/fsb5/extract.py
Executable file
131
python/fsb5/extract.py
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import fsb5
|
||||
|
||||
|
||||
class FSBExtractor:
|
||||
description = 'Extract audio samples from FSB5 files'
|
||||
|
||||
def __init__(self):
|
||||
self.parser = self.init_parser()
|
||||
|
||||
def init_parser(self):
|
||||
parser = argparse.ArgumentParser(description=self.description)
|
||||
parser.add_argument('fsb_file', nargs='*', type=str,
|
||||
help='FSB5 container to extract audio from (defaults to stdin)'
|
||||
)
|
||||
parser.add_argument('-o', '--output-directory', default='out/',
|
||||
help='output directory to write extracted samples into'
|
||||
)
|
||||
parser.add_argument('--verbose', action='store_true',
|
||||
help='be more verbose during extraction'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def print(self, *args):
|
||||
print(*args)
|
||||
|
||||
def debug(self, *args):
|
||||
if self.args.verbose:
|
||||
self.print(*args)
|
||||
|
||||
def error(self, *args):
|
||||
print(*args, file=sys.stderr)
|
||||
|
||||
def write_to_file(self, filename_prefix, filename, extension, contents):
|
||||
directory = self.args.output_directory
|
||||
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
if filename_prefix:
|
||||
path = os.path.join(directory, '{0}-{1}.{2}'.format(filename_prefix, filename, extension))
|
||||
else:
|
||||
path = os.path.join(directory, '{0}.{1}'.format(filename, extension))
|
||||
|
||||
with open(path, 'wb') as f:
|
||||
written = f.write(contents)
|
||||
return path
|
||||
|
||||
def load_fsb(self, data):
|
||||
fsb = fsb5.load(data)
|
||||
ext = fsb.get_sample_extension()
|
||||
|
||||
self.debug('\nHeader:')
|
||||
self.debug('\tVersion: 5.%s' % (fsb.header.version))
|
||||
self.debug('\tSample count: %i' % (fsb.header.numSamples))
|
||||
self.debug('\tNamed samples: %s' % ('Yes' if fsb.header.nameTableSize else 'No'))
|
||||
self.debug('\tSound format: %s' % (fsb.header.mode.name.capitalize()))
|
||||
|
||||
return fsb, ext
|
||||
|
||||
def read_samples(self, fsb_name, fsb, ext):
|
||||
self.debug('Samples:')
|
||||
for sample in fsb.samples:
|
||||
self.debug('\t%s.%s' % (sample.name, ext))
|
||||
self.debug('\tFrequency: %iHz' % (sample.frequency))
|
||||
self.debug('\tChannels: %i' % (sample.channels))
|
||||
self.debug('\tSamples %r' % (sample.samples))
|
||||
|
||||
if sample.metadata and self.args.verbose:
|
||||
for meta_type, meta_value in sample.metadata.items():
|
||||
if type(meta_type) is fsb5.MetadataChunkType:
|
||||
contents = str(meta_value)
|
||||
if len(contents) > 45:
|
||||
contents = contents[:45] + '... )'
|
||||
self.debug('\t%s: %s' % (meta_type.name, contents))
|
||||
else:
|
||||
self.debug('\t<unknown metadata type: %r>' % (meta_type))
|
||||
|
||||
sample_fakepath = '{0}:{1}.{2}'.format(fsb_name, sample.name, ext)
|
||||
try:
|
||||
yield sample_fakepath, sample.name, fsb.rebuild_sample(sample)
|
||||
except ValueError as e:
|
||||
self.error('FAILED to extract %r: %s' % (sample_fakepath, e))
|
||||
|
||||
def handle_file(self, f):
|
||||
data = f.read()
|
||||
fsb_name = os.path.splitext(os.path.basename(f.name))[0]
|
||||
|
||||
self.debug('Reading FSB5 container: %s' % (f.name))
|
||||
|
||||
is_resource = False
|
||||
index = 0
|
||||
while data:
|
||||
fsb, ext = self.load_fsb(data)
|
||||
|
||||
data = data[fsb.raw_size:]
|
||||
if not is_resource and data:
|
||||
is_resource = True
|
||||
|
||||
sample_prefix = fsb_name
|
||||
fakepath_prefix = fsb_name
|
||||
if is_resource:
|
||||
sample_prefix += '-%d' % (index)
|
||||
fakepath_prefix += ':%d' % (index)
|
||||
for sample_fakepath, sample_name, sample_data in self.read_samples(fakepath_prefix, fsb, ext):
|
||||
outpath = self.write_to_file(sample_prefix, sample_name, ext, sample_data)
|
||||
self.print('%r -> %r' % (sample_fakepath, outpath))
|
||||
|
||||
index += 1
|
||||
|
||||
def run(self, args):
|
||||
self.args = self.parser.parse_args(args)
|
||||
|
||||
for fname in self.args.fsb_file:
|
||||
with open(fname, 'rb') as f:
|
||||
self.handle_file(f)
|
||||
|
||||
return 0
|
||||
|
||||
def main():
|
||||
app = FSBExtractor()
|
||||
exit(app.run(sys.argv[1:]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user