-
Notifications
You must be signed in to change notification settings - Fork 40
/
extract.py
executable file
·131 lines (100 loc) · 3.55 KB
/
extract.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
#!/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()