TETRIS3000/source/midi/SoundFont.gd
2019-01-06 15:48:00 +01:00

494 lines
11 KiB
GDScript

"""
SoundFont Reader by Yui Kinomoto @arlez80
"""
"""
SampleLink
"""
const sample_link_mono_sample = 1
const sample_link_right_sample = 2
const sample_link_left_sample = 4
const sample_link_linked_sample = 8
const sample_link_rom_mono_sample = 0x8001
const sample_link_rom_right_sample = 0x8002
const sample_link_rom_left_sample = 0x8004
const sample_link_rom_linked_sample = 0x8008
"""
GenerateOperator
"""
const gen_oper_start_addrs_offset = 0
const gen_oper_end_addrs_offset = 1
const gen_oper_startloop_addrs_offset = 2
const gen_oper_endloop_addrs_offset = 3
const gen_oper_start_addrs_coarse_offset = 4
const gen_oper_mod_lfo_to_pitch = 5
const gen_oper_vib_lfo_to_pitch = 6
const gen_oper_mod_env_to_pitch = 7
const gen_oper_initial_filter_fc = 8
const gen_oper_initial_filter_q = 9
const gen_oper_mod_lfo_to_filter_fc = 10
const gen_oper_mod_env_to_filter_fc = 11
const gen_oper_end_addrs_coarse_offset = 12
const gen_oper_mod_lfo_to_volume = 13
const gen_oper_unused1 = 14
const gen_oper_chorus_effects_send = 15
const gen_oper_reverb_effects_send = 16
const gen_oper_pan = 17
const gen_oper_unused2 = 18
const gen_oper_unused3 = 19
const gen_oper_unused4 = 20
const gen_oper_delay_mod_lfo = 21
const gen_oper_freq_mod_lfo = 22
const gen_oper_delay_vib_lfo = 23
const gen_oper_freq_vib_lfo = 24
const gen_oper_delay_mod_env = 25
const gen_oper_attack_mod_env = 26
const gen_oper_hold_mod_env = 27
const gen_oper_decay_mod_env = 28
const gen_oper_sustain_mod_env = 29
const gen_oper_release_mod_env = 30
const gen_oper_keynum_to_mod_env_hold = 31
const gen_oper_keynum_to_mod_env_decay = 32
const gen_oper_delay_vol_env = 33
const gen_oper_attack_vol_env = 34
const gen_oper_hold_vol_env = 35
const gen_oper_decay_vol_env = 36
const gen_oper_sustain_vol_env = 37
const gen_oper_release_vol_env = 38
const gen_oper_keynum_to_vol_env_hold = 39
const gen_oper_keynum_to_vol_env_decay = 40
const gen_oper_instrument = 41
const gen_oper_reserved1 = 42
const gen_oper_key_range = 43
const gen_oper_vel_range = 44
const gen_oper_startloop_addrs_coarse_offset = 45
const gen_oper_keynum = 46
const gen_oper_velocity = 47
const gen_oper_initial_attenuation = 48
const gen_oper_reserved2 = 49
const gen_oper_endloop_addrs_coarse_offset = 50
const gen_oper_coarse_tune = 51
const gen_oper_fine_tune = 52
const gen_oper_sample_id = 53
const gen_oper_sample_modes = 54
const gen_oper_reserved3 = 55
const gen_oper_scale_tuning = 56
const gen_oper_exclusive_class = 57
const gen_oper_overriding_root_key = 58
const gen_oper_unused5 = 59
const gen_oper_end_oper = 60
"""
SampleMode
"""
const sample_mode_no_loop = 0
const sample_mode_loop_continuously = 1
const sample_mode_unused_no_loop = 2
const sample_mode_loop_ends_by_key_depression = 3
"""
ファイルから読み込み
@param path File path
@return smf
"""
func read_file( path ):
var f = File.new( )
if not f.file_exists( path ):
print( "file %s is not found" % path )
breakpoint
f.open( path, f.READ )
var stream = StreamPeerBuffer.new( )
stream.set_data_array( f.get_buffer( f.get_len( ) ) )
stream.big_endian = false
f.close( )
return self._read( stream )
"""
配列から読み込み
@param data PoolByteArray
@return smf
"""
func read_data( data ):
var stream = StreamPeerBuffer.new( )
stream.set_data_array( data )
stream.big_endian = false
return self._read( stream )
"""
読み込み
@param input
@return SoundFont
"""
func _read( input ):
self._check_chunk( input, "RIFF" )
self._check_header( input, "sfbk" )
var info = self._read_info( input )
var sdta = self._read_sdta( input )
var pdta = self._read_pdta( input )
return {
"info": info,
"sdta": sdta,
"pdta": pdta,
}
"""
チャンクチェック
@param input
@param hdr
"""
func _check_chunk( input, hdr ):
self._check_header( input, hdr )
input.get_32( )
"""
ヘッダーチェック
@param input
@param hdr
"""
func _check_header( input, hdr ):
var chk = input.get_string( 4 )
if hdr != chk:
print( "Doesn't exist " + hdr + " header" )
breakpoint
"""
チャンク読み込み
@param input
@param needs_header
@param chunk
"""
func _read_chunk( stream, needs_header = null ):
var header = stream.get_string( 4 )
if needs_header != null:
if needs_header != header:
print( "Doesn't exist " + needs_header + " header" )
breakpoint
var size = stream.get_u32( )
var new_stream = StreamPeerBuffer.new( )
new_stream.set_data_array( stream.get_partial_data( size )[1] )
new_stream.big_endian = false
return {
"header": header,
"size": size,
"stream": new_stream,
}
"""
INFOチャンクを読み込む
@param stream
@param chunk
"""
func _read_info( stream ):
var chunk = self._read_chunk( stream, "LIST" )
self._check_header( chunk.stream, "INFO" )
var info = {
"ifil":null,
"isng":null,
"inam":null,
"irom":null,
"iver":null,
"icrd":null,
"ieng":null,
"iprd":null,
"icop":null,
"icmt":null,
"isft":null,
}
while 0 < chunk.stream.get_available_bytes( ):
var sub_chunk = self._read_chunk( chunk.stream )
match sub_chunk.header.to_lower( ):
"ifil":
info.ifil = self._read_version_tag( sub_chunk.stream )
"isng":
info.isng = sub_chunk.stream.get_string( sub_chunk.size )
"inam":
info.inam = sub_chunk.stream.get_string( sub_chunk.size )
"irom":
info.irom = sub_chunk.stream.get_string( sub_chunk.size )
"iver":
info.iver = self._read_version_tag( sub_chunk.stream )
"icrd":
info.icrd = sub_chunk.stream.get_string( sub_chunk.size )
"ieng":
info.ieng = sub_chunk.stream.get_string( sub_chunk.size )
"iprd":
info.iprd = sub_chunk.stream.get_string( sub_chunk.size )
"icop":
info.icop = sub_chunk.stream.get_string( sub_chunk.size )
"icmt":
info.icmt = sub_chunk.stream.get_string( sub_chunk.size )
"isft":
info.isft = sub_chunk.stream.get_string( sub_chunk.size )
_:
print( "unknown header" )
breakpoint
return info
"""
バージョンタグを読み込む
@param stream
@param chunk
"""
func _read_version_tag( stream ):
var major = stream.get_u16( )
var minor = stream.get_u16( )
return {
"major": major,
"minor": minor,
}
"""
SDTAを読み込む
@param stream
@param chunk
"""
func _read_sdta( stream ):
var chunk = self._read_chunk( stream, "LIST" )
self._check_header( chunk.stream, "sdta" )
var smpl = self._read_chunk( chunk.stream, "smpl" )
var smpl_bytes = smpl.stream.get_partial_data( smpl.size )[1]
var sm24_bytes = null
if 0 < chunk.stream.get_available_bytes( ):
var sm24_chunk = self._read_chunk( chunk.stream, "sm24" )
sm24_bytes = sm24_chunk.stream.get_partial_data( sm24_chunk.size )[1]
return {
"smpl": smpl_bytes,
"sm24": sm24_bytes,
}
"""
PDTAを読み込む
@param stream
@param chunk
"""
func _read_pdta( stream ):
var chunk = self._read_chunk( stream, "LIST" )
self._check_header( chunk.stream, "pdta" )
var phdr = self._read_pdta_phdr( chunk.stream )
var pbag = self._read_pdta_bag( chunk.stream )
var pmod = self._read_pdta_mod( chunk.stream )
var pgen = self._read_pdta_gen( chunk.stream )
var inst = self._read_pdta_inst( chunk.stream )
var ibag = self._read_pdta_bag( chunk.stream )
var imod = self._read_pdta_mod( chunk.stream )
var igen = self._read_pdta_gen( chunk.stream )
var shdr = self._read_pdta_shdr( chunk.stream )
return {
"phdr": phdr,
"pbag": pbag,
"pmod": pmod,
"pgen": pgen,
"inst": inst,
"ibag": ibag,
"imod": imod,
"igen": igen,
"shdr": shdr,
}
"""
phdr 読み込み
@param stream
@param chunk
"""
func _read_pdta_phdr( stream ):
var chunk = self._read_chunk( stream, "phdr" )
var phdrs = []
while 0 < chunk.stream.get_available_bytes( ):
var phdr = {
"name": "",
"preset": 0,
"bank": 0,
"preset_bag_index": 0,
"library": 0,
"genre": 0,
"morphology": 0,
}
phdr.name = chunk.stream.get_string( 20 )
phdr.preset = chunk.stream.get_u16( )
phdr.bank = chunk.stream.get_u16( )
phdr.preset_bag_index = chunk.stream.get_u16( )
phdr.library = chunk.stream.get_32( )
phdr.genre = chunk.stream.get_32( )
phdr.morphology = chunk.stream.get_32( )
phdrs.append( phdr )
return phdrs
"""
*bag読み込み
@param stream
@param chunk
"""
func _read_pdta_bag( stream ):
var chunk = self._read_chunk( stream )
var bags = []
if chunk.header.substr( 1, 3 ) != "bag":
print( "Doesn't exist *bag header." )
breakpoint
while 0 < chunk.stream.get_available_bytes( ):
var bag = {
"gen_ndx": 0,
"mod_ndx": 0,
}
bag.gen_ndx = chunk.stream.get_u16( )
bag.mod_ndx = chunk.stream.get_u16( )
bags.append( bag )
return bags
"""
*mod読み込み
@param stream
@param chunk
"""
func _read_pdta_mod( stream ):
var chunk = self._read_chunk( stream )
var mods = []
if chunk.header.substr( 1, 3 ) != "mod":
print( "Doesn't exist *mod header." )
breakpoint
while 0 < chunk.stream.get_available_bytes( ):
var mod = {
"src_oper": null,
"dest_oper": 0,
"amount": 0,
"amt_src_oper": null,
"trans_oper": 0,
}
mod.src_oper = self._read_pdta_modulator( chunk.stream.get_u16( ) )
mod.dest_oper = chunk.stream.get_u16( )
mod.amount = chunk.stream.get_u16( )
mod.amt_src_oper = self._read_pdta_modulator( chunk.stream.get_u16( ) )
mod.trans_oper = chunk.stream.get_u16( )
mods.append( mod )
return mods
"""
PDTA-Modulator 読み込み
@param stream
@param chunk
"""
func _read_pdta_modulator( u ):
return {
"type": ( u >> 10 ) & 0x3f,
"direction": ( u >> 8 ) & 0x01,
"polarity": ( u >> 9 ) & 0x01,
"controller": u & 0x7f,
"controllerPallete": ( u >> 7 ) & 0x01,
}
"""
gen 読み込み
@param stream
@param chunk
"""
func _read_pdta_gen( stream ):
var chunk = self._read_chunk( stream )
var gens = []
if chunk.header.substr( 1, 3 ) != "gen":
print( "Doesn't exist *gen header." )
breakpoint
while 0 < chunk.stream.get_available_bytes( ):
var gen = {
"gen_oper": 0,
"amount": 0,
"uamount": 0,
}
gen.gen_oper = chunk.stream.get_u16( )
var u = chunk.stream.get_u16( )
gen.uamount = u
gen.amount = u
if 32767 < u:
gen.amount = - ( 65536 - u )
gens.append( gen )
return gens
"""
inst読み込み
@param stream
@param chunk
"""
func _read_pdta_inst( stream ):
var chunk = self._read_chunk( stream, "inst" )
var insts = []
while 0 < chunk.stream.get_available_bytes( ):
var inst = {
"name": "",
"inst_bag_ndx": 0,
}
inst.name = chunk.stream.get_string( 20 )
inst.inst_bag_ndx = chunk.stream.get_u16( )
insts.append( inst )
return insts
"""
shdr 読み込み
@param stream
@param chunk
"""
func _read_pdta_shdr( stream ):
var chunk = self._read_chunk( stream, "shdr" )
var shdrs = []
while 0 < chunk.stream.get_available_bytes( ):
var shdr = {
"name": "",
"start": 0,
"end": 0,
"start_loop": 0,
"end_loop": 0,
"sample_rate": 0,
"original_key": 0,
"pitch_correction": 0,
"sample_link": 0,
"sample_type": 0,
}
shdr.name = chunk.stream.get_string( 20 )
shdr.start = chunk.stream.get_u32( )
shdr.end = chunk.stream.get_u32( )
shdr.start_loop = chunk.stream.get_u32( )
shdr.end_loop = chunk.stream.get_u32( )
shdr.sample_rate = chunk.stream.get_u32( )
shdr.original_key = chunk.stream.get_u8( )
shdr.pitch_correction = chunk.stream.get_8( )
shdr.sample_link = chunk.stream.get_u16( )
shdr.sample_type = chunk.stream.get_u16( )
shdrs.append( shdr )
return shdrs