使用 AKSampleDescriptor
Using AKSampleDescriptor
使用 AKSamplerDescriptor
我正在使用经过改编的 AKSampler 示例,我在其中尝试使用 Fluid.sf3 melodicSounds 的 sforzando 输出。 Sforzando 为每个乐器创建 .sfz 文件,但所有这些文件都指向一个巨大的 .wav 文件的全局样本。
在所有 instrument.sfz 文件中,有一个要使用的 wave 文件部分的偏移量和端点描述。
加载 .sfz 文件时,由于内存问题导致崩溃。似乎对于 .sfz 文件中的每个定义区域,都会再次加载完整的 .wav 文件 (140 mB)。
最有可能的是,像 AKSampler 示例中那样使用 AKSampleDescriptor 加载示例文件将在重新加载完整的 .wav 文件时忽略偏移量和端点(AKSampleDescriptor.startPoint 和 AKSampleDescriptor.endPoint)。
有没有办法从样本文件中从头到尾只加载所需的部分,因为完整的文件包含所有乐器的样本数据(我知道并使用复音,它只提取一个乐器在时间和工作正常,但这是其他用途)
或者,这对我来说似乎是最好的,只需加载一次文件,然后让样本描述符指向内存中的数据
好建议,罗布。我自己刚刚 运行 进入这个 one-giant-WAV 问题,以前从未见过它。我还使用 Sforzando 进行转换。我将研究向 AKSampler 添加必要的功能。与此同时,编写一个程序将一个 WAV 文件切割成更小的片段并相应地调整 SFZ 可能会更容易。
这里有一些 Python 2.7 代码来执行此操作,我已经成功地将其用于 Sforzando 转换的 sf2 音色。它可能需要更改才能为您工作——sfz 文件之间存在巨大差异——但至少它可以帮助您入门。此代码需要 PyDub 库来处理 WAV 音频。
import os
import re
from pydub import AudioSegment
def stripComments(text):
def replacer(match):
s = match.group(0)
if s.startswith('/'):
return " " # note: a space and not an empty string
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\.|[^\\'])*\'|"(?:\.|[^\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)
def updateSplitList(splitList, regionLabels, values):
if len(values) > 3:
start = int(values['offset'])
length = int(values['end']) - start
name = regionLabels.pop(0)
splitList.add((name, start, length))
def lookupSplitName(splitList, offset, end):
for (name, start, end) in splitList:
if offset == start and end == end:
return name
return None
def outputGroupAndRegion(outputFile, splitList, values):
if values.has_key('lokey') and values.has_key('hikey') and values.has_key('pitch_keycenter'):
outputFile.write('<group> lokey=%s hikey=%s pitch_keycenter=%s\n' % (values['lokey'], values['hikey'], values['pitch_keycenter']))
elif values.has_key('key') and values.has_key('pitch_keycenter'):
outputFile.write('<group> key=%s pitch_keycenter=%s\n' % (values['key'], values['pitch_keycenter']))
if len(values) > 3:
outputFile.write(' <region> ')
if values.has_key('lovel') and values.has_key('hivel'):
outputFile.write('lovel=%s hivel=%s ' % (values['lovel'], values['hivel']))
if values.has_key('tune'):
outputFile.write('tune=%s ' % values['tune'])
if values.has_key('volume'):
outputFile.write('volume=%s ' % values['volume'])
if values.has_key('offset'):
outputFile.write('offset=0 ')
if values.has_key('end'):
outputFile.write('end=%d ' % (int(values['end']) - int(values['offset'])))
if values.has_key('loop_mode'):
outputFile.write('loop_mode=%s ' % values['loop_mode'])
if values.has_key('loop_start'):
outputFile.write('loop_start=%d ' % (int(values['loop_start']) - int(values['offset'])))
if values.has_key('loop_end'):
outputFile.write('loop_end=%d ' % (int(values['loop_end']) - int(values['offset'])))
outputFile.write('sample=samples/%s' % lookupSplitName(splitList, int(values['offset']), int(values['end'])) + '.wav\n')
def process(inputFile, outputFile):
# create a list of region labels
regionLabels = list()
for line in open(inputFile):
if line.strip().startswith('region_label'):
regionLabels.append(line.strip().split('=')[1])
# read entire input SFZ file
sfz = open(inputFile).read()
# strip comments and create a mixed list of <header> tags and key=value pairs
sfz_list = stripComments(sfz).split()
inSection = "none"
default_path = ""
global_sample = None
values = dict()
splitList = set()
# parse the input SFZ data and build up splitList
for item in sfz_list:
if item.startswith('<'):
inSection = item
updateSplitList(splitList, regionLabels, values)
values.clear()
continue
elif item.find('=') < 0:
#print 'unknown:', item
continue
key, value = item.split('=')
if inSection == '<control>' and key == 'default_path':
default_path = value.replace('\', '/')
elif inSection == '<global>' and key == 'sample':
global_sample = value.replace('\', '/')
elif inSection == '<region>':
values[key] = value
# split the wav file
bigWav = AudioSegment.from_wav(global_sample)
#print "%d channels, %d bytes/sample, %d frames/sec" % (bigWav.channels, bigWav.sample_width, bigWav.frame_rate)
frate = float(bigWav.frame_rate)
for (name, start, length) in splitList:
startMs = 1000 * start / frate
endMs = 1000 * (start + length) / frate
wav = bigWav[startMs : endMs]
wavName = 'samples/' + name + '.wav'
wav.export(wavName, format='wav')
# parse the input SFZ data again and generate the output SFZ
for item in sfz_list:
if item.startswith('<'):
inSection = item
outputGroupAndRegion(outputFile, splitList, values)
values.clear()
continue
elif item.find('=') < 0:
#print 'unknown:', item
continue
key, value = item.split('=')
if inSection == '<control>' and key == 'default_path':
default_path = value.replace('\', '/')
elif inSection == '<global>' and key == 'sample':
global_sample = value.replace('\', '/')
elif inSection == '<region>':
values[key] = value
dirPath = '000'
fileNameList = os.listdir(dirPath)
for fileName in fileNameList:
if fileName.endswith('.sfz'):
inputFile = os.path.join(dirPath, fileName)
outputFile = open(fileName, 'w')
print fileName
process(inputFile, outputFile)
使用 AKSamplerDescriptor
我正在使用经过改编的 AKSampler 示例,我在其中尝试使用 Fluid.sf3 melodicSounds 的 sforzando 输出。 Sforzando 为每个乐器创建 .sfz 文件,但所有这些文件都指向一个巨大的 .wav 文件的全局样本。
在所有 instrument.sfz 文件中,有一个要使用的 wave 文件部分的偏移量和端点描述。
加载 .sfz 文件时,由于内存问题导致崩溃。似乎对于 .sfz 文件中的每个定义区域,都会再次加载完整的 .wav 文件 (140 mB)。
最有可能的是,像 AKSampler 示例中那样使用 AKSampleDescriptor 加载示例文件将在重新加载完整的 .wav 文件时忽略偏移量和端点(AKSampleDescriptor.startPoint 和 AKSampleDescriptor.endPoint)。
有没有办法从样本文件中从头到尾只加载所需的部分,因为完整的文件包含所有乐器的样本数据(我知道并使用复音,它只提取一个乐器在时间和工作正常,但这是其他用途)
或者,这对我来说似乎是最好的,只需加载一次文件,然后让样本描述符指向内存中的数据
好建议,罗布。我自己刚刚 运行 进入这个 one-giant-WAV 问题,以前从未见过它。我还使用 Sforzando 进行转换。我将研究向 AKSampler 添加必要的功能。与此同时,编写一个程序将一个 WAV 文件切割成更小的片段并相应地调整 SFZ 可能会更容易。
这里有一些 Python 2.7 代码来执行此操作,我已经成功地将其用于 Sforzando 转换的 sf2 音色。它可能需要更改才能为您工作——sfz 文件之间存在巨大差异——但至少它可以帮助您入门。此代码需要 PyDub 库来处理 WAV 音频。
import os
import re
from pydub import AudioSegment
def stripComments(text):
def replacer(match):
s = match.group(0)
if s.startswith('/'):
return " " # note: a space and not an empty string
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\.|[^\\'])*\'|"(?:\.|[^\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)
def updateSplitList(splitList, regionLabels, values):
if len(values) > 3:
start = int(values['offset'])
length = int(values['end']) - start
name = regionLabels.pop(0)
splitList.add((name, start, length))
def lookupSplitName(splitList, offset, end):
for (name, start, end) in splitList:
if offset == start and end == end:
return name
return None
def outputGroupAndRegion(outputFile, splitList, values):
if values.has_key('lokey') and values.has_key('hikey') and values.has_key('pitch_keycenter'):
outputFile.write('<group> lokey=%s hikey=%s pitch_keycenter=%s\n' % (values['lokey'], values['hikey'], values['pitch_keycenter']))
elif values.has_key('key') and values.has_key('pitch_keycenter'):
outputFile.write('<group> key=%s pitch_keycenter=%s\n' % (values['key'], values['pitch_keycenter']))
if len(values) > 3:
outputFile.write(' <region> ')
if values.has_key('lovel') and values.has_key('hivel'):
outputFile.write('lovel=%s hivel=%s ' % (values['lovel'], values['hivel']))
if values.has_key('tune'):
outputFile.write('tune=%s ' % values['tune'])
if values.has_key('volume'):
outputFile.write('volume=%s ' % values['volume'])
if values.has_key('offset'):
outputFile.write('offset=0 ')
if values.has_key('end'):
outputFile.write('end=%d ' % (int(values['end']) - int(values['offset'])))
if values.has_key('loop_mode'):
outputFile.write('loop_mode=%s ' % values['loop_mode'])
if values.has_key('loop_start'):
outputFile.write('loop_start=%d ' % (int(values['loop_start']) - int(values['offset'])))
if values.has_key('loop_end'):
outputFile.write('loop_end=%d ' % (int(values['loop_end']) - int(values['offset'])))
outputFile.write('sample=samples/%s' % lookupSplitName(splitList, int(values['offset']), int(values['end'])) + '.wav\n')
def process(inputFile, outputFile):
# create a list of region labels
regionLabels = list()
for line in open(inputFile):
if line.strip().startswith('region_label'):
regionLabels.append(line.strip().split('=')[1])
# read entire input SFZ file
sfz = open(inputFile).read()
# strip comments and create a mixed list of <header> tags and key=value pairs
sfz_list = stripComments(sfz).split()
inSection = "none"
default_path = ""
global_sample = None
values = dict()
splitList = set()
# parse the input SFZ data and build up splitList
for item in sfz_list:
if item.startswith('<'):
inSection = item
updateSplitList(splitList, regionLabels, values)
values.clear()
continue
elif item.find('=') < 0:
#print 'unknown:', item
continue
key, value = item.split('=')
if inSection == '<control>' and key == 'default_path':
default_path = value.replace('\', '/')
elif inSection == '<global>' and key == 'sample':
global_sample = value.replace('\', '/')
elif inSection == '<region>':
values[key] = value
# split the wav file
bigWav = AudioSegment.from_wav(global_sample)
#print "%d channels, %d bytes/sample, %d frames/sec" % (bigWav.channels, bigWav.sample_width, bigWav.frame_rate)
frate = float(bigWav.frame_rate)
for (name, start, length) in splitList:
startMs = 1000 * start / frate
endMs = 1000 * (start + length) / frate
wav = bigWav[startMs : endMs]
wavName = 'samples/' + name + '.wav'
wav.export(wavName, format='wav')
# parse the input SFZ data again and generate the output SFZ
for item in sfz_list:
if item.startswith('<'):
inSection = item
outputGroupAndRegion(outputFile, splitList, values)
values.clear()
continue
elif item.find('=') < 0:
#print 'unknown:', item
continue
key, value = item.split('=')
if inSection == '<control>' and key == 'default_path':
default_path = value.replace('\', '/')
elif inSection == '<global>' and key == 'sample':
global_sample = value.replace('\', '/')
elif inSection == '<region>':
values[key] = value
dirPath = '000'
fileNameList = os.listdir(dirPath)
for fileName in fileNameList:
if fileName.endswith('.sfz'):
inputFile = os.path.join(dirPath, fileName)
outputFile = open(fileName, 'w')
print fileName
process(inputFile, outputFile)