327 lines
10 KiB
GDScript
327 lines
10 KiB
GDScript
"""
|
|
Sound Bank by Yui Kinomoto @arlez80
|
|
"""
|
|
|
|
const drum_track_bank = 128
|
|
const SoundFont = preload( "SoundFont.gd" )
|
|
|
|
# デフォルト
|
|
var default_mix_rate_table = [819,868,920,974,1032,1094,1159,1228,1301,1378,1460,1547,1639,1736,1840,1949,2065,2188,2318,2456,2602,2756,2920,3094,3278,3473,3679,3898,4130,4375,4635,4911,5203,5513,5840,6188,6556,6945,7358,7796,8259,8751,9271,9822,10406,11025,11681,12375,13111,13891,14717,15592,16519,17501,18542,19644,20812,22050,23361,24750,26222,27781,29433,31183,33038,35002,37084,39289,41625,44100,46722,49501,52444,55563,58866,62367,66075,70004,74167,78577,83250,88200,93445,99001,104888,111125,117733,124734,132151,140009,148334,157155,166499,176400,186889,198002,209776,222250,235466,249467,264301,280018,296668,314309,332999,352800,373779,396005,419552,444500,470932,498935,528603,560035,593337,628618,665998,705600,747557,792009,839105,889000,941863,997869,1057205,1120070,1186673,1257236]
|
|
var default_ads_state = [
|
|
{ "time": 0, "volume": 1.0 },
|
|
{ "time": 0.2, "volume": 0.95 },
|
|
]
|
|
var default_release_state = [
|
|
{ "time": 0, "volume": 0.95 },
|
|
{ "time": 0.01, "volume": 0.0 },
|
|
]
|
|
|
|
# 音色テーブル
|
|
var presets = {}
|
|
|
|
"""
|
|
楽器
|
|
"""
|
|
func create_preset( ):
|
|
var instruments = []
|
|
for i in range( 0, 128 ):
|
|
instruments.append( null )
|
|
return {
|
|
"name": "",
|
|
"number": 0,
|
|
"instruments": instruments,
|
|
"bags": [],
|
|
}
|
|
|
|
"""
|
|
ノート
|
|
"""
|
|
func create_instrument( ):
|
|
return {
|
|
"mix_rate": 44100,
|
|
"stream": null,
|
|
"ads_state": default_ads_state,
|
|
"release_state": default_release_state,
|
|
"preset": null,
|
|
# "assine_group": 0, # reserved
|
|
}
|
|
|
|
"""
|
|
再生周波数計算
|
|
"""
|
|
func calc_mix_rate( rate, center_key, target_key ):
|
|
return round( rate * pow( 2.0, ( target_key - center_key ) / 12.0 ) )
|
|
|
|
"""
|
|
追加
|
|
"""
|
|
func set_preset_sample( program_number, base_sample, base_mix_rate ):
|
|
var mix_rate_table = default_mix_rate_table
|
|
|
|
if base_mix_rate != 44100:
|
|
print( "not implemented" )
|
|
breakpoint
|
|
|
|
var preset = self.create_preset( )
|
|
preset.name = "#%03d" % program_number
|
|
preset.number = program_number
|
|
for i in range(0,128):
|
|
var inst = self.create_instrument( )
|
|
inst.mix_rate = mix_rate_table[i]
|
|
inst.stream = base_sample
|
|
inst.preset = preset
|
|
preset.instruments[i] = inst
|
|
|
|
self.set_preset( program_number, preset )
|
|
|
|
"""
|
|
追加
|
|
"""
|
|
func set_preset( program_number, preset ):
|
|
self.presets[program_number] = preset
|
|
|
|
"""
|
|
指定した楽器を取得
|
|
"""
|
|
func get_preset( program_number, bank = 0 ):
|
|
var pc = program_number | ( bank << 7 )
|
|
|
|
if not self.presets.has( pc ):
|
|
if drum_track_bank == bank:
|
|
pc = drum_track_bank
|
|
else:
|
|
pc = program_number
|
|
if not self.presets.has( pc ):
|
|
pc = self.presets.keys( )[0]
|
|
|
|
var preset = self.presets[pc]
|
|
return preset
|
|
|
|
"""
|
|
サウンドフォント読み込み
|
|
"""
|
|
func read_soundfont( sf, need_program_numbers = null ):
|
|
var sf_insts = self._read_soundfont_pdta_inst( sf )
|
|
|
|
var bag_index = 0
|
|
var gen_index = 0
|
|
for phdr_index in range( 0, len( sf.pdta.phdr )-1 ):
|
|
var phdr = sf.pdta.phdr[phdr_index]
|
|
|
|
var preset = self.create_preset( )
|
|
var program_number = phdr.preset | ( phdr.bank << 7 )
|
|
|
|
preset.name = phdr.name
|
|
preset.number = program_number
|
|
|
|
var bag_next = sf.pdta.phdr[phdr_index+1].preset_bag_index
|
|
var bag_count = bag_index
|
|
while bag_count < bag_next:
|
|
var gen_next = sf.pdta.pbag[bag_count+1].gen_ndx
|
|
var gen_count = gen_index
|
|
var bag = {
|
|
"preset": preset,
|
|
"coarse_tune": 0,
|
|
"fine_tune": 0,
|
|
"key_range": null,
|
|
"instrument": null,
|
|
"pan": 0.5,
|
|
}
|
|
while gen_count < gen_next:
|
|
var gen = sf.pdta.pgen[gen_count]
|
|
match gen.gen_oper:
|
|
SoundFont.gen_oper_coarse_tune:
|
|
bag.coarse_tune = gen.amount
|
|
SoundFont.gen_oper_fine_tune:
|
|
bag.fine_tune = gen.amount
|
|
SoundFont.gen_oper_key_range:
|
|
bag.key_range = {
|
|
"high": gen.uamount >> 8,
|
|
"low": gen.uamount & 0xFF,
|
|
}
|
|
SoundFont.gen_oper_pan:
|
|
bag.pan = ( gen.amount + 500 ) / 1000.0
|
|
SoundFont.gen_oper_instrument:
|
|
bag.instrument = sf_insts[gen.uamount]
|
|
gen_count += 1
|
|
if bag.instrument != null:
|
|
preset.bags.append( bag )
|
|
gen_index = gen_next
|
|
bag_count += 1
|
|
bag_index = bag_next
|
|
|
|
# 追加するか?
|
|
if need_program_numbers != null:
|
|
if not( program_number in need_program_numbers ) and not( phdr.preset in need_program_numbers ):
|
|
continue
|
|
# 追加
|
|
self._read_soundfont_preset_compose_sample( sf, preset )
|
|
self.set_preset( program_number, preset )
|
|
|
|
func _read_soundfont_preset_compose_sample( sf, preset ):
|
|
"""
|
|
var samples = []
|
|
var zero_4bytes = PoolIntArray( [ 0, 0 ] )
|
|
for bag in preset.bags[0].instrument.bags:
|
|
var sample = PoolIntArray( )
|
|
var start = bag.sample.start + bag.sample_start_offset
|
|
var end = bag.sample.end + bag.sample_end_offset
|
|
for i in range( 0, ( end - start ) / 2 ):
|
|
sample.append_array( zero_4bytes )
|
|
samples.append( sample )
|
|
"""
|
|
|
|
for pbag_index in range( 0, preset.bags.size( ) ):
|
|
var pbag = preset.bags[pbag_index]
|
|
for ibag_index in range( 0, pbag.instrument.bags.size( ) ):
|
|
var ibag = pbag.instrument.bags[ibag_index]
|
|
var start = ibag.sample.start + ibag.sample_start_offset
|
|
var end = ibag.sample.end + ibag.sample_end_offset
|
|
var start_loop = ibag.sample.start_loop + ibag.sample_start_loop_offset
|
|
var end_loop = ibag.sample.end_loop + ibag.sample_end_loop_offset
|
|
var mix_rate = ibag.sample.sample_rate * pow( 2.0, ( pbag.coarse_tune + ibag.coarse_tune ) / 12.0 ) * pow( 2.0, ( pbag.fine_tune + ibag.sample.pitch_correction + ibag.fine_tune ) / 1200.0 )
|
|
|
|
var ass = AudioStreamSample.new( )
|
|
ass.data = sf.sdta.smpl.subarray( start * 2, end * 2 )
|
|
ass.format = AudioStreamSample.FORMAT_16_BITS
|
|
ass.mix_rate = mix_rate
|
|
ass.stereo = false #bag.sample.sample_type != SoundFont.sample_link_mono_sample
|
|
ass.loop_begin = start_loop - start
|
|
ass.loop_end = end_loop - start
|
|
if ibag.sample_modes == SoundFont.sample_mode_no_loop or ibag.sample_modes == SoundFont.sample_mode_unused_no_loop:
|
|
ass.loop_mode = AudioStreamSample.LOOP_DISABLED
|
|
else:
|
|
ass.loop_mode = AudioStreamSample.LOOP_FORWARD
|
|
var key_range = ibag.key_range
|
|
if pbag.key_range != null:
|
|
key_range = pbag.key_range
|
|
for key_number in range( key_range.low, key_range.high + 1 ):
|
|
#if preset.number == drum_track_bank << 7:
|
|
# if 36 <= key_number and key_number <= 40:
|
|
# print( key_number, " # ", ibag.sample.name );
|
|
if preset.instruments[key_number] != null:
|
|
continue
|
|
var instrument = self.create_instrument( )
|
|
instrument.preset = preset
|
|
if ibag.original_key == 255:
|
|
instrument.mix_rate = ibag.sample.sample_rate
|
|
else:
|
|
instrument.mix_rate = self.calc_mix_rate( mix_rate, ibag.original_key, key_number )
|
|
instrument.stream = ass
|
|
|
|
var a = ibag.adsr.attack_vol_env_time
|
|
var d = ibag.adsr.decay_vol_env_time
|
|
var s = ibag.adsr.sustain_vol_env_level
|
|
var r = ibag.adsr.release_vol_env_time
|
|
instrument.ads_state = [
|
|
{ "time": 0, "volume": 0.0 },
|
|
{ "time": a, "volume": 1.0 },
|
|
{ "time": a+d, "volume": s },
|
|
]
|
|
instrument.release_state = [
|
|
{ "time": 0, "volume": s },
|
|
{ "time": r, "volume": 0.0 },
|
|
]
|
|
preset.instruments[key_number] = instrument
|
|
|
|
func _read_soundfont_pdta_inst( sf ):
|
|
var sf_insts = []
|
|
var bag_index = 0
|
|
var gen_index = 0
|
|
|
|
for inst_index in range(0, len( sf.pdta.inst ) - 1 ):
|
|
var inst = sf.pdta.inst[inst_index]
|
|
var sf_inst = {"name": inst.name, "bags": [] }
|
|
|
|
var bag_next = sf.pdta.inst[inst_index+1].inst_bag_ndx
|
|
var bag_count = bag_index
|
|
var global_bag = {}
|
|
while bag_count < bag_next:
|
|
var bag = {
|
|
"sample": null,
|
|
"sample_id": -1,
|
|
"sample_start_offset": 0,
|
|
"sample_end_offset": 0,
|
|
"sample_start_loop_offset": 0,
|
|
"sample_end_loop_offset": 0,
|
|
"coarse_tune": 0,
|
|
"fine_tune": 0,
|
|
"original_key": 255,
|
|
"keynum": 0,
|
|
"sample_modes": 0,
|
|
"key_range": { "high": 127, "low": 0 },
|
|
"vel_range": { "high": 127, "low": 0 },
|
|
"adsr": {
|
|
"attack_vol_env_time": 0.001,
|
|
"decay_vol_env_time": 0.001,
|
|
"sustain_vol_env_level": 1.0,
|
|
"release_vol_env_time": 0.001,
|
|
},
|
|
}
|
|
var gen_next = sf.pdta.ibag[bag_count+1].gen_ndx
|
|
var gen_count = gen_index
|
|
while gen_count < gen_next:
|
|
var gen = sf.pdta.igen[gen_count]
|
|
match gen.gen_oper:
|
|
SoundFont.gen_oper_key_range:
|
|
bag.key_range.high = gen.uamount >> 8
|
|
bag.key_range.low = gen.uamount & 0xFF
|
|
SoundFont.gen_oper_vel_range:
|
|
bag.vel_range.high = gen.uamount >> 8
|
|
bag.vel_range.low = gen.uamount & 0xFF
|
|
SoundFont.gen_oper_overriding_root_key:
|
|
bag.original_key = gen.amount
|
|
SoundFont.gen_oper_start_addrs_offset:
|
|
bag.sample_start_offset += gen.amount
|
|
SoundFont.gen_oper_end_addrs_offset:
|
|
bag.sample_end_offset += gen.amount
|
|
SoundFont.gen_oper_start_addrs_coarse_offset:
|
|
bag.sample_start_offset += gen.amount * 32768
|
|
SoundFont.gen_oper_end_addrs_coarse_offset:
|
|
bag.sample_end_offset += gen.amount * 32768
|
|
SoundFont.gen_oper_startloop_addrs_offset:
|
|
bag.sample_start_loop_offset += gen.amount
|
|
SoundFont.gen_oper_endloop_addrs_offset:
|
|
bag.sample_end_loop_offset += gen.amount
|
|
SoundFont.gen_oper_startloop_addrs_coarse_offset:
|
|
bag.sample_start_loop_offset += gen.amount * 32768
|
|
SoundFont.gen_oper_endloop_addrs_coarse_offset:
|
|
bag.sample_end_loop_offset += gen.amount * 32768
|
|
SoundFont.gen_oper_coarse_tune:
|
|
bag.coarse_tune = gen.amount
|
|
SoundFont.gen_oper_fine_tune:
|
|
bag.fine_tune = gen.amount
|
|
SoundFont.gen_oper_keynum:
|
|
bag.keynum = gen.amount
|
|
SoundFont.gen_oper_attack_vol_env:
|
|
bag.adsr.attack_vol_env_time = pow( 2.0, gen.amount / 1200.0 )
|
|
SoundFont.gen_oper_decay_vol_env:
|
|
bag.adsr.decay_vol_env_time = pow( 2.0, gen.amount / 1200.0 )
|
|
SoundFont.gen_oper_release_vol_env:
|
|
bag.adsr.release_vol_env_time = pow( 2.0, gen.amount / 1200.0 )
|
|
SoundFont.gen_oper_sustain_vol_env:
|
|
var s = min( max( 0, gen.amount ), 1440 )
|
|
bag.adsr.sustain_vol_env_level = ( 1440.0 - s ) / 1440.0
|
|
SoundFont.gen_oper_sample_modes:
|
|
bag.sample_modes = gen.uamount
|
|
SoundFont.gen_oper_sample_id:
|
|
bag.sample_id = gen.uamount
|
|
bag.sample = sf.pdta.shdr[gen.amount]
|
|
if bag.original_key == 255:
|
|
bag.original_key = bag.sample.original_key
|
|
#_:
|
|
# print( gen.gen_oper )
|
|
gen_count += 1
|
|
# global zoneでない場合
|
|
if bag.sample != null:
|
|
sf_inst.bags.append( bag )
|
|
else:
|
|
global_bag = bag
|
|
gen_index = gen_next
|
|
bag_count += 1
|
|
sf_insts.append( sf_inst )
|
|
bag_index = bag_next
|
|
|
|
return sf_insts
|
|
|