"""
	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