Midi play

This commit is contained in:
adrienmalin 2018-12-31 17:09:09 +01:00
parent 600555a80b
commit 6993e4bd92
25 changed files with 2230 additions and 165 deletions

Binary file not shown.

View File

@ -1,15 +0,0 @@
[remap]
importer="ogg_vorbis"
type="AudioStreamOGGVorbis"
path="res://.import/Song A pizzicato solo.ogg-bb0592fa96a1944827b2c377836a7301.oggstr"
[deps]
source_file="res://Audio/Song A pizzicato solo.ogg"
dest_files=[ "res://.import/Song A pizzicato solo.ogg-bb0592fa96a1944827b2c377836a7301.oggstr" ]
[params]
loop=false
loop_offset=0

Binary file not shown.

View File

@ -1,15 +0,0 @@
[remap]
importer="ogg_vorbis"
type="AudioStreamOGGVorbis"
path="res://.import/Song A without pizzicato.ogg-0762944017b6daf2dd37510d2a86a592.oggstr"
[deps]
source_file="res://Audio/Song A without pizzicato.ogg"
dest_files=[ "res://.import/Song A without pizzicato.ogg-0762944017b6daf2dd37510d2a86a592.oggstr" ]
[params]
loop=false
loop_offset=0

Binary file not shown.

Binary file not shown.

View File

@ -1,21 +0,0 @@
[remap]
importer="wav"
type="AudioStreamSample"
path="res://.import/line_clear.wav-7797a050b9c9acfadee761ccba5d496d.sample"
[deps]
source_file="res://Audio/line_clear.wav"
dest_files=[ "res://.import/line_clear.wav-7797a050b9c9acfadee761ccba5d496d.sample" ]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=true
edit/normalize=true
edit/loop=false
compress/mode=0

Binary file not shown.

View File

@ -1,21 +0,0 @@
[remap]
importer="wav"
type="AudioStreamSample"
path="res://.import/tetris.wav-72e7c256c9dbf193e3a65319af7e85d5.sample"
[deps]
source_file="res://Audio/tetris.wav"
dest_files=[ "res://.import/tetris.wav-72e7c256c9dbf193e3a65319af7e85d5.sample" ]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=true
edit/normalize=true
edit/loop=false
compress/mode=0

View File

@ -10,12 +10,21 @@ const TetroS = preload("res://Tetrominos/TetroS.tscn")
const TetroT = preload("res://Tetrominos/TetroT.tscn") const TetroT = preload("res://Tetrominos/TetroT.tscn")
const TetroZ = preload("res://Tetrominos/TetroZ.tscn") const TetroZ = preload("res://Tetrominos/TetroZ.tscn")
const NB_LINES = 20
const NB_COLLUMNS = 10
const EMPTY_CELL = -1 const EMPTY_CELL = -1
const NB_MINOES = 4
const NEXT_POSITION = Vector3(13, 16, 0) const NEXT_POSITION = Vector3(13, 16, 0)
const START_POSITION = Vector3(5, 20, 0) const START_POSITION = Vector3(5, 20, 0)
const HOLD_POSITION = Vector3(-5, 16, 0) const HOLD_POSITION = Vector3(-5, 16, 0)
const SOUND_POSITION = Vector3(5, 10, 6)
const movements = {
"move_right": Vector3(1, 0, 0),
"move_left": Vector3(-1, 0, 0),
"soft_drop": Vector3(0, -1, 0)
}
const SCORES = [ const SCORES = [
[0, 4, 1], [0, 4, 1],
[1, 8, 2], [1, 8, 2],
@ -24,25 +33,23 @@ const SCORES = [
[8] [8]
] ]
const LINES_CLEARED_NAMES = ["", "SINGLE", "DOUBLE", "TRIPLE", "TETRIS"] const LINES_CLEARED_NAMES = ["", "SINGLE", "DOUBLE", "TRIPLE", "TETRIS"]
const T_SPIN_NAMES = ["", "MINI T-SPIN", "T-SPIN"] const T_SPIN_NAMES = ["", "T-SPIN", "MINI T-SPIN"]
const NB_LINES = 20
const NB_COLLUMNS = 10 const MIDI_MOVE_CHANNELS = [7, 8, 9, 11]
const MIDI_LINE_CLEAR_CHANNELS = [2, 6, 10]
var next_piece = random_piece() var next_piece = random_piece()
var current_piece var current_piece
var held_piece var held_piece
var current_piece_held = false var current_piece_held = false
var locked = false
var autoshift_action = "" var autoshift_action = ""
var movements = {
"move_right": Vector3(1, 0, 0),
"move_left": Vector3(-1, 0, 0),
"soft_drop": Vector3(0, -1, 0)
}
var exploding_lines = [] var exploding_lines = []
var lines_to_clear = [] var lines_to_clear = []
var random_bag = [] var random_bag = []
var playing = true var playing = true
var level = 0 var level = 0
var goal = 0 var goal = 0
var score = 0 var score = 0
@ -84,7 +91,6 @@ func new_piece():
current_piece.emit_trail(true) current_piece.emit_trail(true)
autoshift_action = "" autoshift_action = ""
update_ghost_piece() update_ghost_piece()
$Music2.translation = SOUND_POSITION
next_piece = random_piece() next_piece = random_piece()
next_piece.translation = NEXT_POSITION next_piece.translation = NEXT_POSITION
if move(movements["soft_drop"]): if move(movements["soft_drop"]):
@ -113,21 +119,21 @@ func process_actions():
if action != autoshift_action: if action != autoshift_action:
if Input.is_action_pressed(action): if Input.is_action_pressed(action):
if move(movements[action]): if move(movements[action]):
move_music() move_midi()
autoshift_action = action autoshift_action = action
$AutoShiftTimer.stop() $AutoShiftTimer.stop()
$AutoShiftDelay.start() $AutoShiftDelay.start()
if Input.is_action_just_pressed("hard_drop"): if Input.is_action_just_pressed("hard_drop"):
move_music() move_midi()
while move(movements["soft_drop"]): while move(movements["soft_drop"]):
pass pass
lock_piece() lock_piece()
if Input.is_action_just_pressed("rotate_clockwise"): if Input.is_action_just_pressed("rotate_clockwise"):
rotate(Tetromino.CLOCKWISE) rotate(Tetromino.CLOCKWISE)
move_music() move_midi()
if Input.is_action_just_pressed("rotate_counterclockwise"): if Input.is_action_just_pressed("rotate_counterclockwise"):
rotate(Tetromino.COUNTERCLOCKWISE) rotate(Tetromino.COUNTERCLOCKWISE)
move_music() move_midi()
if Input.is_action_just_pressed("hold"): if Input.is_action_just_pressed("hold"):
hold() hold()
@ -154,7 +160,7 @@ func possible_positions(initial_positions, movement):
position = initial_positions[i] + movement position = initial_positions[i] + movement
if is_free_cell(position): if is_free_cell(position):
test_positions.append(position) test_positions.append(position)
if test_positions.size() == NB_MINOES: if test_positions.size() == Tetromino.NB_MINOES:
return test_positions return test_positions
else: else:
return [] return []
@ -164,7 +170,6 @@ func move(movement):
$LockDelay.start() $LockDelay.start()
if movement.x: if movement.x:
update_ghost_piece() update_ghost_piece()
$Music2.translate(movement)
return true return true
else: else:
return false return false
@ -177,9 +182,11 @@ func rotate(direction):
else: else:
return false return false
func move_music(): func move_midi():
AudioServer.set_bus_mute(AudioServer.get_bus_index("Music2"), false) for channel_id in MIDI_MOVE_CHANNELS:
$MusicDelay.start() $MidiPlayer.channel_status[channel_id].pan = current_piece.translation.x / 10.0
mute_midi_channel(MIDI_MOVE_CHANNELS, false)
$MidiPlayer/MoveDelay.start()
func update_ghost_piece(): func update_ghost_piece():
var new_positions = current_piece.positions() var new_positions = current_piece.positions()
@ -225,10 +232,13 @@ func update_score():
score += 100 * s score += 100 * s
goal -= s goal -= s
print(T_SPIN_NAMES[current_piece.t_spin], ' ', LINES_CLEARED_NAMES[lines_to_clear.size()], " Score ", score) print(T_SPIN_NAMES[current_piece.t_spin], ' ', LINES_CLEARED_NAMES[lines_to_clear.size()], " Score ", score)
mute_midi_channel(MIDI_LINE_CLEAR_CHANNELS, false)
$MidiPlayer.play_now()
if lines_to_clear.size() == Tetromino.NB_MINOES: if lines_to_clear.size() == Tetromino.NB_MINOES:
$TetrisSFX.play() $MidiPlayer/LineLcearDelay.wait_time = 1.71
else: else:
$LineCLearSFX.play() $MidiPlayer/LineLcearDelay.wait_time = 0.86
$MidiPlayer/LineLcearDelay.start()
if goal <= 0: if goal <= 0:
new_level() new_level()
else: else:
@ -250,7 +260,6 @@ func hold():
current_piece.translation = START_POSITION current_piece.translation = START_POSITION
current_piece.emit_trail(true) current_piece.emit_trail(true)
update_ghost_piece() update_ghost_piece()
$Music2.translation = SOUND_POSITION
else: else:
held_piece = current_piece held_piece = current_piece
new_piece() new_piece()
@ -262,15 +271,16 @@ func resume():
playing = true playing = true
$DropTimer.start() $DropTimer.start()
$LockDelay.start() $LockDelay.start()
start_musics() $MidiPlayer.play()
mute_midi_channel(MIDI_MOVE_CHANNELS, true)
mute_midi_channel(MIDI_LINE_CLEAR_CHANNELS, true)
print("RESUME") print("RESUME")
func pause(): func pause():
playing = false playing = false
$DropTimer.stop() $DropTimer.stop()
$LockDelay.stop() $LockDelay.stop()
$Music.stop() $MidiPlayer.stop()
$Music2.stop()
print("PAUSE") print("PAUSE")
func game_over(): func game_over():
@ -284,13 +294,12 @@ func _notification(what):
if what == MainLoop.NOTIFICATION_WM_FOCUS_OUT: if what == MainLoop.NOTIFICATION_WM_FOCUS_OUT:
pause() pause()
func _on_MusicDelay_timeout(): func mute_midi_channel(channels, muted):
AudioServer.set_bus_mute(AudioServer.get_bus_index("Music2"), true) for channel_id in channels:
$MidiPlayer.channel_mute[channel_id] = muted
func _on_Music_finished(): func _on_MoveDelay_timeout():
start_musics() mute_midi_channel(MIDI_MOVE_CHANNELS, true)
func start_musics(): func _on_LineLcearDelay_timeout():
$Music.play() mute_midi_channel(MIDI_LINE_CLEAR_CHANNELS, true)
$Music2.play()
AudioServer.set_bus_mute(AudioServer.get_bus_index("Music2"), true)

View File

@ -1,14 +1,11 @@
[gd_scene load_steps=11 format=2] [gd_scene load_steps=8 format=2]
[ext_resource path="res://Mino/MinoLibrary.tres" type="MeshLibrary" id=1] [ext_resource path="res://Mino/MinoLibrary.tres" type="MeshLibrary" id=1]
[ext_resource path="res://GridMap/GridMap.gd" type="Script" id=2] [ext_resource path="res://GridMap/GridMap.gd" type="Script" id=2]
[ext_resource path="res://GridMap/GridBack.tscn" type="PackedScene" id=3] [ext_resource path="res://GridMap/GridBack.tscn" type="PackedScene" id=3]
[ext_resource path="res://GridMap/BackMaterial.tres" type="Material" id=4] [ext_resource path="res://GridMap/BackMaterial.tres" type="Material" id=4]
[ext_resource path="res://Tetrominos/GhostPiece.tscn" type="PackedScene" id=5] [ext_resource path="res://Tetrominos/GhostPiece.tscn" type="PackedScene" id=5]
[ext_resource path="res://Audio/Song A without pizzicato.ogg" type="AudioStream" id=6] [ext_resource path="res://midi/MidiPlayer.tscn" type="PackedScene" id=6]
[ext_resource path="res://Audio/Song A pizzicato solo.ogg" type="AudioStream" id=7]
[ext_resource path="res://Audio/line_clear.wav" type="AudioStream" id=8]
[ext_resource path="res://Audio/tetris.wav" type="AudioStream" id=9]
[sub_resource type="CubeMesh" id=1] [sub_resource type="CubeMesh" id=1]
@ -95,62 +92,26 @@ mesh = SubResource( 1 )
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, 0 ) transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, 0 )
_sections_unfolded = [ "Transform" ] _sections_unfolded = [ "Transform" ]
[node name="Music" type="AudioStreamPlayer" parent="." index="9"] [node name="MidiPlayer" parent="." index="9" instance=ExtResource( 6 )]
stream = ExtResource( 6 ) file = "res://midi/Tetris - Song A.mid"
volume_db = 0.0 volume_db = -3
pitch_scale = 1.0 loop = true
autoplay = false soundfont = "res://midi/TimGM6mb.sf2"
mix_target = 0
bus = "Music"
_sections_unfolded = [ "Attenuation Filter", "Doppler", "Pause" ]
[node name="Music2" type="AudioStreamPlayer3D" parent="." index="10"] [node name="MoveDelay" type="Timer" parent="MidiPlayer" index="1"]
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, 0 )
stream = ExtResource( 7 )
attenuation_model = 0
unit_db = 0.0
unit_size = 1.0
max_db = 6.0
pitch_scale = 1.0
autoplay = false
max_distance = 0.0
out_of_range_mode = 0
bus = "Music2"
area_mask = 1
emission_angle_enabled = false
emission_angle_degrees = 45.0
emission_angle_filter_attenuation_db = -12.0
attenuation_filter_cutoff_hz = 5000.0
attenuation_filter_db = -24.0
doppler_tracking = 0
_sections_unfolded = [ "Attenuation Filter", "Doppler" ]
[node name="MusicDelay" type="Timer" parent="." index="11"]
process_mode = 1 process_mode = 1
wait_time = 0.1 wait_time = 0.1
one_shot = true one_shot = true
autostart = false autostart = false
[node name="LineCLearSFX" type="AudioStreamPlayer" parent="." index="12"] [node name="LineLcearDelay" type="Timer" parent="MidiPlayer" index="2"]
stream = ExtResource( 8 ) process_mode = 1
volume_db = 0.0 wait_time = 0.86
pitch_scale = 1.0 one_shot = false
autoplay = false autostart = false
mix_target = 0
bus = "SFX"
[node name="TetrisSFX" type="AudioStreamPlayer" parent="." index="13"]
stream = ExtResource( 9 )
volume_db = 0.0
pitch_scale = 1.0
autoplay = false
mix_target = 0
bus = "SFX"
[connection signal="timeout" from="DropTimer" to="." method="_on_DropTimer_timeout"] [connection signal="timeout" from="DropTimer" to="." method="_on_DropTimer_timeout"]
@ -162,8 +123,8 @@ bus = "SFX"
[connection signal="timeout" from="ExplosionDelay" to="." method="_on_ExplosionDelay_timeout"] [connection signal="timeout" from="ExplosionDelay" to="." method="_on_ExplosionDelay_timeout"]
[connection signal="finished" from="Music" to="." method="_on_Music_finished"] [connection signal="timeout" from="MidiPlayer/MoveDelay" to="." method="_on_MoveDelay_timeout"]
[connection signal="timeout" from="MusicDelay" to="." method="_on_MusicDelay_timeout"] [connection signal="timeout" from="MidiPlayer/LineLcearDelay" to="." method="_on_LineLcearDelay_timeout"]

3
MusicMidiPlayer.gd Normal file
View File

@ -0,0 +1,3 @@
extends "MidiPlayer.gd"
export var channel_mute = [false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false]

View File

@ -89,7 +89,7 @@ adjustment_contrast = 1.0
adjustment_saturation = 0.34 adjustment_saturation = 0.34
_sections_unfolded = [ "Adjustments", "Ambient Light", "Background", "Fog", "Resource" ] _sections_unfolded = [ "Adjustments", "Ambient Light", "Background", "Fog", "Resource" ]
[node name="WorldEnvironment" type="WorldEnvironment"] [node name="WorldEnvironment" type="WorldEnvironment" index="0"]
environment = SubResource( 2 ) environment = SubResource( 2 )

View File

@ -1,4 +1,21 @@
[gd_resource type="AudioBusLayout" format=2] [gd_resource type="AudioBusLayout" load_steps=2 format=2]
[sub_resource type="AudioEffectDelay" id=1]
resource_name = "Delay"
dry = 1.0
tap1/active = true
tap1/delay_ms = 250.0
tap1/level_db = -6.0
tap1/pan = 0.2
tap2/active = true
tap2/delay_ms = 500.0
tap2/level_db = -12.0
tap2/pan = -0.4
feedback/active = false
feedback/delay_ms = 340.0
feedback/level_db = -6.0
feedback/lowpass = 16000.0
[resource] [resource]
@ -14,12 +31,14 @@ bus/1/mute = false
bus/1/bypass_fx = false bus/1/bypass_fx = false
bus/1/volume_db = -16.8 bus/1/volume_db = -16.8
bus/1/send = "Master" bus/1/send = "Master"
bus/2/name = "Music2" bus/2/name = "LineClear"
bus/2/solo = false bus/2/solo = false
bus/2/mute = false bus/2/mute = false
bus/2/bypass_fx = false bus/2/bypass_fx = false
bus/2/volume_db = 5.5 bus/2/volume_db = 5.5
bus/2/send = "Master" bus/2/send = "Master"
bus/2/effect/0/effect = SubResource( 1 )
bus/2/effect/0/enabled = true
bus/3/name = "SFX" bus/3/name = "SFX"
bus/3/solo = false bus/3/solo = false
bus/3/mute = false bus/3/mute = false

96
midi/ADSR.gd Normal file
View File

@ -0,0 +1,96 @@
extends AudioStreamPlayer
"""
AudioStreamPlayer with ADSR
"""
var releasing = false
var instrument = null
var velocity = 0
var pitch_bend = 0
var mix_rate = 0
var using_timer = 0.0
var timer = 0.0
var current_volume = 0
var maximum_volume_db = -8.0
var minimum_volume_db = -108.0
var pan = 0.5
var ads_state = [
{ "time": 0, "volume": 1.0 },
{ "time": 0.2, "volume": 0.95 },
# { "time": 0.2, "jump_to": 0.0 }, # not implemented
]
var release_state = [
{ "time": 0, "volume": 0.8 },
{ "time": 0.01, "volume": 0.0 },
# { "time": 0.2, "jump_to": 0.0 }, # not implemented
]
func _ready( ):
self.stop( )
func set_instrument( instrument ):
self.instrument = instrument
self.mix_rate = instrument.mix_rate
self.stream = instrument.stream.duplicate( )
self.ads_state = instrument.ads_state
self.release_state = instrument.release_state
func play( ):
self.releasing = false
self.timer = 0.0
self.using_timer = 0.0
self.current_volume = self.ads_state[0].volume
self.stream.mix_rate = round( self.mix_rate * ( 1.0 + self.pitch_bend * 0.5 ) )
.play( 0.0 )
self._update_volume( )
func start_release( ):
self.releasing = true
self.current_volume = self.release_state[0].volume
self.timer = 0.0
self._update_volume( )
func set_pitch_bend( pb ):
self.pitch_bend = pb
var pos = self.get_playback_position( )
self.stream.mix_rate = round( self.mix_rate * ( 1.0 + self.pitch_bend * 0.5 ) )
.play( pos )
func _process( delta ):
if not self.playing:
return
self.timer += delta
self.using_timer += delta
# self.transform.origin.x = self.pan * self.get_viewport( ).size.x
# ADSR
var use_state = null
if self.releasing:
use_state = self.release_state
else:
use_state = self.ads_state
var all_states = use_state.size( )
var last_state = all_states - 1
if use_state[last_state].time <= self.timer:
self.current_volume = use_state[last_state].volume
if self.releasing:
self.stop( )
else:
for state_number in range( 1, all_states ):
var state = use_state[state_number]
if self.timer < state.time:
var pre_state = use_state[state_number-1]
var s = ( state.time - self.timer ) / ( state.time - pre_state.time )
var t = 1.0 - s
self.current_volume = pre_state.volume * s + state.volume * t
break
self._update_volume( )
func _update_volume( ):
var s = self.current_volume
var t = 1.0 - s
self.volume_db = s * self.maximum_volume_db + t * self.minimum_volume_db

15
midi/ADSR.tscn Normal file
View File

@ -0,0 +1,15 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://midi/ADSR.gd" type="Script" id=1]
[node name="ADSR" type="AudioStreamPlayer"]
stream = null
volume_db = 0.0
autoplay = false
mix_target = 0
bus = "Master"
script = ExtResource( 1 )
_sections_unfolded = [ "Pause", "Transform", "Z Index" ]

326
midi/Bank.gd Normal file
View File

@ -0,0 +1,326 @@
"""
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

140
midi/MOD.gd Normal file
View File

@ -0,0 +1,140 @@
"""
MOD reader by Yui Kinomoto @arlez80
"""
"""
@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 = true
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 = true
return self._read( stream )
"""
@param stream
@return smf
"""
func _read( stream ):
var name = self._read_string( stream, 20 )
var samples = self._read_sample_informations( stream )
var song_length = stream.get_u8( )
var unknown_number = stream.get_u8( )
var song_positions = stream.get_partial_data( 128 )[1]
var max_song_position = 0
for sp in song_positions:
if max_song_position < sp:
max_song_position = sp
var magic = self._read_string( stream, 4 )
var channel_count = 4
match magic:
"6CHN":
channel_count = 6
"FLT8", "8CHN", "CD81", "OKTA":
channel_count = 8
"16CN":
channel_count = 16
"32CN":
channel_count = 32
_:
# print( "Unknown magic" )
# breakpoint
pass
var patterns = self._read_patterns( stream, max_song_position, channel_count )
self._read_sample_data( stream, samples )
return {
"name": name,
"song_length": song_length,
"unknown_number": unknown_number,
"song_positions": song_positions,
"magic": magic,
"patterns": patterns,
"samples": samples,
}
"""
"""
func _read_sample_informations( stream ):
var samples = []
for i in range( 0, 31 ):
var sample = {}
sample.name = self._read_string( stream, 22 )
sample.length = stream.get_u16( ) * 2
sample.fine_tune = stream.get_u8( ) & 0x0F
if 0x08 < sample.fine_tune:
sample.fine_tune = 0x10 - sample.fine_tune
sample.volume = stream.get_u8( )
sample.loop_start = stream.get_u16( ) * 2
sample.loop_length = stream.get_u16( ) * 2
samples.append( sample )
return samples
"""
"""
func _read_patterns( stream, max_position, channels ):
var patterns = []
for position in range( 0, max_position ):
var pattern = []
for i in range( 0, 64 ):
var line = []
for ch in range( 0, channels ):
var v1 = stream.get_u16( )
var v2 = stream.get_u16( )
var sample_number = ( ( v1 >> 8 ) & 0xF0 ) | ( ( v2 >> 12 ) & 0x0F )
var key_number = v1 & 0x0FFF
var effect_command = v2 & 0x0FFF
line.append({
"sample_number": sample_number,
"key_number": key_number,
"effect_command": effect_command,
})
pattern.append( line )
patterns.append( pattern )
"""
"""
func _read_sample_data( stream, samples ):
for sample in samples:
sample.data = stream.get_partial_data( sample.length )[1]
"""
@param stream Stream
@param size string size
@return string
"""
func _read_string( stream, size ):
return stream.get_partial_data( size )[1].get_string_from_ascii( )

411
midi/MidiPlayer.gd Normal file
View File

@ -0,0 +1,411 @@
extends Node
const max_track = 16
const max_channel = 16
const max_note_number = 128
const max_program_number = 128
const drum_track_channel = 0x09
const drum_track_bank = 128
const ADSR = preload( "ADSR.tscn" )
const SMF = preload( "SMF.gd" )
const SoundFont = preload( "SoundFont.gd" )
const Bank = preload( "Bank.gd" )
export var max_polyphony = 64
export var file = ""
export var playing = false
export var channel_mute = [false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false]
export var play_speed = 1.0
export var volume_db = -30
export var key_shift = 0
export var loop = false
export var loop_start = 0
export var soundfont = ""
export var mix_target = AudioStreamPlayer.MIX_TARGET_STEREO
export var bus = "Master"
var smf_data = null
var tempo = 120 setget set_tempo
var seconds_to_timebase = 2.3
var position = 0
var last_position = 0
var track_status = null
var channel_status = []
var channel_volume_db = 20
var bank = null
var audio_stream_players = []
var _used_program_numbers = []
var play_later_events = {2: {}, 6:{}, 10:{}}
signal changed_tempo( tempo )
signal appeared_lyric( lyric )
signal appeared_marker( marker )
signal appeared_cue_point( cue_point )
signal looped
func _ready( ):
if self.playing:
self.play( )
"""
"""
func _prepare_to_play( ):
# ファイル読み込み
if self.smf_data == null:
var smf_reader = SMF.new( )
self.smf_data = smf_reader.read_file( self.file )
self._init_track( )
self._analyse_smf( )
self._init_channel( )
# 楽器
if self.bank == null:
self.bank = Bank.new( )
if self.soundfont != "":
var sf_reader = SoundFont.new( )
var sf2 = sf_reader.read_file( self.soundfont )
self.bank.read_soundfont( sf2, self._used_program_numbers )
# 発音機
if self.audio_stream_players.size( ) == 0:
for i in range( self.max_polyphony ):
var audio_stream_player = ADSR.instance( )
audio_stream_player.mix_target = self.mix_target
audio_stream_player.bus = self.bus
self.add_child( audio_stream_player )
self.audio_stream_players.append( audio_stream_player )
else:
for audio_stream_player in self.audio_stream_players:
audio_stream_player.mix_target = self.mix_target
audio_stream_player.bus = self.bus
"""
"""
class TrackSorter:
static func sort(a, b):
if a.time < b.time:
return true
elif b.time < a.time:
return false
else:
if a.event.type == SMF.MIDIEventType.note_off:
return true
elif b.event.type == SMF.MIDIEventType.note_off:
return false
func _init_track( ):
self.track_status = {
"events": [],
"event_pointer": 0,
}
for track in self.smf_data.tracks:
self.track_status.events += track.events
if 1 < self.smf_data.tracks.size( ):
self.track_status.events.sort_custom( TrackSorter, "sort" )
self.last_position = self.track_status.events[len(self.track_status.events)-1].time
"""
SMF解析
"""
func _analyse_smf( ):
var channels = []
for i in range( max_channel ):
channels.append({
#"note_on": {},
#"program_number": 0,
"number": i,
"bank": 0,
})
self._used_program_numbers = []
for event_chunk in self.track_status.events:
var channel_number = event_chunk.channel_number
var channel = channels[channel_number]
var event = event_chunk.event
match event.type:
#SMF.MIDIEventType.note_off:
# channel.note_on.erase( event.note )
#SMF.MIDIEventType.note_on:
# channel.note_on[event.note] = true
SMF.MIDIEventType.program_change:
var program_number = event.number | ( channel.bank << 7 )
# channel.program_number = program_number
if not( event.number in self._used_program_numbers ):
self._used_program_numbers.append( event.number )
if not( program_number in self._used_program_numbers ):
self._used_program_numbers.append( program_number )
SMF.MIDIEventType.control_change:
match event.number:
SMF.control_number_bank_select_msb:
if channel.number == drum_track_channel:
channel.bank = drum_track_bank
else:
channel.bank = ( channel.bank & 0x7F ) | ( event.value << 7 )
SMF.control_number_bank_select_lsb:
if channel.number == drum_track_channel:
channel.bank = drum_track_bank
else:
channel.bank = ( channel.bank & 0x3F80 ) | ( event.value & 0x7F )
SMF.control_number_tkool_loop_point:
self.loop_start = event_chunk.time
"""
"""
func _init_channel( ):
self.channel_status = []
for i in range( max_channel ):
var drum_track = ( i == drum_track_channel )
var bank = 0
if drum_track:
bank = self.drum_track_bank
self.channel_status.append({
"number": i,
"note_on": {},
"bank": bank,
"program": 0,
"volume": 1.0,
"expression": 1.0,
"pitch_bend": 0.0,
"drum_track": drum_track,
"pan": 0.5,
})
"""
@param from_position
"""
func play( from_position = 0 ):
self._prepare_to_play( )
self.playing = true
self.seek( from_position )
"""
"""
func seek( to_position ):
self._stop_all_notes( )
self.position = to_position
var pointer = 0
var length = len(self.track_status.events)
while pointer < length:
var event_chunk = self.track_status.events[pointer]
if self.position < event_chunk.time:
break
pointer += 1
self.track_status.event_pointer = pointer
"""
"""
func stop( ):
self._stop_all_notes( )
self.playing = false
"""
"""
func set_tempo( bpm ):
tempo = bpm
self.seconds_to_timebase = tempo / 60.0
self.emit_signal( "changed_tempo", bpm )
"""
"""
func _stop_all_notes( ):
for audio_stream_player in self.audio_stream_players:
audio_stream_player.stop( )
for channel in self.channel_status:
channel.note_on = {}
"""
"""
func _process( delta ):
if self.smf_data == null:
return
if not self.playing:
return
self.position += self.smf_data.timebase * delta * self.seconds_to_timebase * self.play_speed
self._process_track( )
"""
"""
func _process_track( ):
var track = self.track_status
if track.events == null:
return
var length = len(track.events)
if length <= track.event_pointer:
if self.loop:
self.seek( self.loop_start )
self.emit_signal( "looped" )
else:
self.playing = false
return
for channel in self.channel_status:
for key_number in channel.note_on.keys( ):
var note_on = channel.note_on[key_number]
if not note_on.playing:
channel.note_on.erase( key_number )
while track.event_pointer < length:
var event_chunk = track.events[track.event_pointer]
if self.position < event_chunk.time:
break
track.event_pointer += 1
var channel = self.channel_status[event_chunk.channel_number]
var event = event_chunk.event
match event.type:
SMF.MIDIEventType.note_off:
self._process_track_event_note_off( channel, event )
if event_chunk.channel_number in play_later_events:
play_later_events[event_chunk.channel_number].erase(event.note)
SMF.MIDIEventType.note_on:
self._process_track_event_note_on( channel, event )
if event_chunk.channel_number in play_later_events:
play_later_events[event_chunk.channel_number][event.note] = event
SMF.MIDIEventType.program_change:
channel.program = event.number
SMF.MIDIEventType.control_change:
self._process_track_event_control_change( channel, event )
SMF.MIDIEventType.pitch_bend:
channel.pitch_bend = event.value / 8192.0 - 1.0
self._update_pitch_bend_note( channel )
SMF.MIDIEventType.system_event:
self._process_track_system_event( channel, event )
_:
# 無視
pass
func play_now():
for channel in play_later_events:
for note in play_later_events[channel]:
_process_track_event_note_on( channel_status[channel], play_later_events[channel][note] )
func _process_track_event_note_off( channel, event ):
var key_number = event.note + self.key_shift
if channel.note_on.has( key_number ):
var note_player = channel.note_on[key_number]
if note_player != null:
note_player.start_release( )
channel.note_on.erase( key_number )
func _process_track_event_note_on( channel, event ):
if not self.channel_mute[channel.number]:
var key_number = event.note + self.key_shift
var note_volume = channel.volume * channel.expression * ( event.velocity / 127.0 )
var volume_db = note_volume * self.channel_volume_db - self.channel_volume_db + self.volume_db
var preset = self.bank.get_preset( channel.program, channel.bank )
var instrument = preset.instruments[key_number]
if instrument != null:
if channel.note_on.has( key_number ):
channel.note_on[key_number].start_release( )
var note_player = self._get_idle_player( channel.program )
if note_player != null:
note_player.velocity = event.velocity
note_player.maximum_volume_db = volume_db
note_player.pitch_bend = channel.pitch_bend
note_player.set_instrument( instrument )
note_player.play( )
if not channel.drum_track:
channel.note_on[key_number] = note_player
func _process_track_event_control_change( channel, event ):
match event.number:
SMF.control_number_volume:
channel.volume = event.value / 127.0
self._update_volume_note( channel )
SMF.control_number_expression:
channel.expression = event.value / 127.0
self._update_volume_note( channel )
SMF.control_number_pan:
channel.pan = event.value / 127.0
SMF.control_number_bank_select_msb:
if channel.drum_track:
channel.bank = self.drum_track_bank
else:
channel.bank = ( channel.bank & 0x7F ) | ( event.value << 7 )
SMF.control_number_bank_select_lsb:
if channel.drum_track:
channel.bank = self.drum_track_bank
else:
channel.bank = ( channel.bank & 0x3F80 ) | ( event.value & 0x7F )
_:
# 無視
pass
func _process_track_system_event( channel, event ):
match event.args.type:
SMF.MIDISystemEventType.set_tempo:
self.tempo = 60000000.0 / event.args.bpm
SMF.MIDISystemEventType.lyric:
self.emit_signal( "appeared_lyric", event.args.text )
SMF.MIDISystemEventType.marker:
self.emit_signal( "appeared_marker", event.args.text )
SMF.MIDISystemEventType.cue_point:
self.emit_signal( "appeared_cue_point", event.args.text )
_:
# 無視
pass
func _get_idle_player( program ):
var stopped_audio_stream_player = null
var minimum_volume = 100.0
var oldest_audio_stream_player = null
var oldest = 0.0
for audio_stream_player in self.audio_stream_players:
if not audio_stream_player.playing:
return audio_stream_player
if audio_stream_player.releasing and audio_stream_player.current_volume < minimum_volume:
stopped_audio_stream_player = audio_stream_player
minimum_volume = audio_stream_player.current_volume
if oldest < audio_stream_player.using_timer:
oldest_audio_stream_player = audio_stream_player
oldest = audio_stream_player.using_timer
if stopped_audio_stream_player != null:
return stopped_audio_stream_player
return oldest_audio_stream_player
func _update_volume_note( channel ):
for note in channel.note_on.values( ):
var note_volume = channel.volume * channel.expression * ( note.velocity / 127.0 )
var volume_db = note_volume * self.channel_volume_db - self.channel_volume_db + self.volume_db
note.maximum_volume_db = volume_db
func _update_pitch_bend_note( channel ):
for note in channel.note_on.values( ):
note.set_pitch_bend( channel.pitch_bend )
"""
"""
func get_now_playing_polyphony( ):
var polyphony = 0
for audio_stream_player in self.audio_stream_players:
if audio_stream_player.playing:
polyphony += 1
return polyphony

30
midi/MidiPlayer.tscn Normal file
View File

@ -0,0 +1,30 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://midi/MidiPlayer.gd" type="Script" id=1]
[node name="MidiPlayer" type="Node" index="0"]
script = ExtResource( 1 )
max_polyphony = 64
file = ""
playing = false
channel_mute = [ false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false ]
play_speed = 1.0
volume_db = -30
key_shift = 0
loop = false
loop_start = 0
soundfont = ""
mix_target = 0
bus = "Master"
[node name="Default" type="AudioStreamPlayer" parent="." index="0"]
stream = null
volume_db = 0.0
pitch_scale = 1.0
autoplay = false
mix_target = 0
bus = "Master"

634
midi/SMF.gd Normal file
View File

@ -0,0 +1,634 @@
"""
SMF reader/writer by Yui Kinomoto @arlez80
"""
# -----------------------------------------------------------------------------
# 定数
# Control Numbers
const control_number_bank_select_msb = 0x00
const control_number_modulation = 0x01
const control_number_breath_controller = 0x02
const control_number_foot_controller = 0x04
const control_number_portamento_time = 0x05
const control_number_data_entry = 0x06
const control_number_volume = 0x07
const control_number_balance = 0x08
const control_number_pan = 0x0A
const control_number_expression = 0x0B
const control_number_bank_select_lsb = 0x20
const control_number_modulation_lsb = 0x21
const control_number_breath_controller_lsb = 0x22
const control_number_foot_controller_lsb = 0x24
const control_number_portamento_time_lsb = 0x25
const control_number_data_entry_lsb = 0x26
const control_number_channel_volume_lsb = 0x27
const control_number_calance_lsb = 0x28
const control_number_pan_lsb = 0x2A
const control_number_expression_lsb = 0x2B
const control_number_hold = 0x40
const control_number_portament = 0x41
const control_number_sostenuto = 0x42
const control_number_soft_pedal = 0x43
const control_number_legato_foot_switch = 0x44
const control_number_freeze = 0x45
const control_number_sound_variation = 0x46
const control_number_timbre = 0x47
const control_number_release_time = 0x48
const control_number_attack_time = 0x49
const control_number_brightness = 0x4A
const control_number_vibrato_rate = 0x4B
const control_number_vibrato_depth = 0x4C
const control_number_vibrato_delay = 0x4D
const control_number_nrpn_lsb = 0x62
const control_number_nrpn_msb = 0x63
const control_number_rpn_lsb = 0x64
const control_number_rpn_msb = 0x65
const control_number_tkool_loop_point = 0x6F # CC111
# Manufacture ID
const manufacture_id_universal_nopn_realtime_sys_ex = 0x7E
const manufacture_id_universal_realtime_sys_ex = 0x7F
const manufacture_id_kawai_musical_instruments_mfg_co_ltd = 0x40
const manufacture_id_roland_corporation = 0x41
const manufacture_id_korg_inc = 0x42
const manufacture_id_yamaha_corporation = 0x43
const manufacture_id_casio_computer_co_ltd = 0x44
const manufacture_id_kamiya_studio_co_ltd = 0x46
const manufacture_id_akai_electric_co_ltd = 0x47
# Enums
enum MIDIEventType {
note_off, # 8*
note_on, # 9*
polyphonic_key_pressure, # A*
control_change, # B*
program_change, # C*
channel_pressure, # D*
pitch_bend, # E*
system_event, # F*
}
enum MIDISystemEventType {
sys_ex,
divided_sys_ex,
text_event, # 01
copyright, # 02
track_name, # 03
instrument_name, # 04
lyric, # 05
marker, # 06
cue_point, # 07
midi_channel_prefix, # 20
end_of_track, # 2F
set_tempo, # 51
smpte_offset, # 54
beat, # 58
key, # 59
unknown,
}
# -----------------------------------------------------------------------------
# 読み込み : Reader
var last_event_type
"""
@param path File path
@return smf or null(read error)
"""
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 = true
f.close( )
return self._read( stream )
"""
@param data PoolByteArray
@return smf or null(read error)
"""
func read_data( data ):
var stream = StreamPeerBuffer.new( )
stream.set_data_array( data )
stream.big_endian = true
return self._read( stream )
"""
@param input
@return smf
"""
func _read( input ):
var header = self._read_chunk_data( input )
if header.id != "MThd" and header.size != 6:
print( "expected MThd header" )
return null
var format_type = header.stream.get_u16( )
var track_count = header.stream.get_u16( )
var timebase = header.stream.get_u16( )
var tracks = []
for i in range( 0, track_count ):
var track = self._read_track( input, i )
if track == null:
return null
tracks.append( track )
return {
"format_type": format_type,
"track_count": track_count,
"timebase": timebase,
"tracks": tracks,
}
"""
@param input
@param track_number
@return track data or null(read error)
"""
func _read_track( input, track_number ):
var track_chunk = self._read_chunk_data( input )
if track_chunk.id != "MTrk":
print( "Unknown chunk: " + track_chunk.id )
return null
var stream = track_chunk.stream
var time = 0
var events = []
while 0 < stream.get_available_bytes( ):
var delta_time = self._read_variable_int( stream )
time += delta_time
var event_type_byte = stream.get_u8( )
var event
if self._is_system_event( event_type_byte ):
var args = self._read_system_event( stream, event_type_byte )
if args == null: return null
event = {
"type": MIDIEventType.system_event,
"args": args
}
else:
event = self._read_event( stream, event_type_byte )
if event == null: return null
if ( event_type_byte & 0x80 ) == 0:
event_type_byte = self.last_event_type
events.append({
"time": time,
"channel_number": event_type_byte & 0x0f,
"event": event,
})
return {
"track_number": track_number,
"events": events,
}
"""
@param b event type
@return trueを返す
"""
func _is_system_event( b ):
return ( b & 0xf0 ) == 0xf0
"""
"""
func _read_system_event( stream, event_type_byte ):
if event_type_byte == 0xff:
var meta_type = stream.get_u8( )
var size = self._read_variable_int( stream )
match meta_type:
0x01:
return { "type": MIDISystemEventType.text_event, "text": self._read_string( stream, size ) }
0x02:
return { "type": MIDISystemEventType.copyright, "text": self._read_string( stream, size ) }
0x03:
return { "type": MIDISystemEventType.track_name, "text": self._read_string( stream, size ) }
0x04:
return { "type": MIDISystemEventType.instrument_name, "text": self._read_string( stream, size ) }
0x05:
return { "type": MIDISystemEventType.lyric, "text": self._read_string( stream, size ) }
0x06:
return { "type": MIDISystemEventType.marker, "text": self._read_string( stream, size ) }
0x07:
return { "type": MIDISystemEventType.cue_point, "text": self._read_string( stream, size ) }
0x20:
if size != 1:
print( "MIDI Channel Prefix length is not 1" )
return null
return { "type": MIDISystemEventType.midi_channel_prefix, "prefix": stream.get_u8( ) }
0x2F:
if size != 0:
print( "End of track with unknown data" )
return null
return { "type": MIDISystemEventType.end_of_track }
0x51:
if size != 3:
print( "Tempo length is not 3" )
return null
# beat per microseconds
var bpm = stream.get_u8( ) << 16
bpm |= stream.get_u8( ) << 8
bpm |= stream.get_u8( )
return { "type": MIDISystemEventType.set_tempo, "bpm": bpm }
0x54:
if size != 5:
print( "SMPTE length is not 5" )
return null
var hr = stream.get_u8( )
var mm = stream.get_u8( )
var se = stream.get_u8( )
var fr = stream.get_u8( )
var ff = stream.get_u8( )
return {
"type": MIDISystemEventType.smpte_offset,
"hr": hr,
"mm": mm,
"se": se,
"fr": fr,
"ff": ff,
}
0x58:
if size != 4:
print( "Beat length is not 4" )
return null
var numerator = stream.get_u8( )
var denominator = stream.get_u8( )
var clock = stream.get_u8( )
var beat32 = stream.get_u8( )
return {
"type": MIDISystemEventType.beat,
"numerator": numerator,
"denominator": denominator,
"clock": clock,
"beat32": beat32,
}
0x59:
if size != 2:
print( "Key length is not 2" )
return null
var sf = stream.get_u8( )
var minor = stream.get_u8( ) == 1
return {
"type": MIDISystemEventType.key,
"sf": sf,
"minor": minor,
}
_:
return {
"type": MIDISystemEventType.unknown,
"meta_type": meta_type,
"data": stream.get_partial_data( size )[1],
}
elif event_type_byte == 0xf0:
var size = self._read_variable_int( stream )
return {
"type": MIDISystemEventType.sys_ex,
"data": stream.get_partial_data( size )[1],
}
elif event_type_byte == 0xf7:
var size = self._read_variable_int( stream )
return {
"type": MIDISystemEventType.divided_sys_ex,
"data": stream.get_partial_data( size )[1],
}
print( "Unknown system event type: %x" % event_type_byte )
return null
"""
@param stream
@param event_type_byte
@return MIDIEvent
"""
func _read_event( stream, event_type_byte ):
var param = 0
if ( event_type_byte & 0x80 ) == 0:
# running status
param = event_type_byte
event_type_byte = self.last_event_type
else:
param = stream.get_u8( )
self.last_event_type = event_type_byte
var event_type = event_type_byte & 0xf0
match event_type:
0x80:
return {
"type": MIDIEventType.note_off,
"note": param,
"velocity": stream.get_u8( ),
}
0x90:
var velocity = stream.get_u8( )
if velocity == 0:
# velocity0のnote_onはnote_off扱いにする
return {
"type": MIDIEventType.note_off,
"note": param,
"velocity": velocity,
}
else:
return {
"type": MIDIEventType.note_on,
"note": param,
"velocity": velocity,
}
0xA0:
return {
"type": MIDIEventType.polyphonic_key_pressure,
"note": param,
"value": stream.get_u8( ),
}
0xB0:
return {
"type": MIDIEventType.control_change,
"number": param,
"value": stream.get_u8( ),
}
0xC0:
return {
"type": MIDIEventType.program_change,
"number": param,
}
0xD0:
return {
"type": MIDIEventType.channel_pressure,
"value": param,
}
0xE0:
return {
"type": MIDIEventType.pitch_bend,
"value": param | ( stream.get_u8( ) << 7 ),
}
print( "unknown event type: %d" % event_type_byte )
return null
"""
@param stream
@return
"""
func _read_variable_int( stream ):
var result = 0
while true:
var c = stream.get_u8( )
if ( c & 0x80 ) != 0:
result |= c & 0x7f
result <<= 7
else:
result |= c
break
return result
"""
@param stream Stream
@return chunk data
"""
func _read_chunk_data( stream ):
var id = self._read_string( stream, 4 )
var size = stream.get_32( )
var new_stream = StreamPeerBuffer.new( )
new_stream.set_data_array( stream.get_partial_data( size )[1] )
new_stream.big_endian = true
return {
"id": id,
"size": size,
"stream": new_stream
}
"""
@param stream Stream
@param size string size
@return string
"""
func _read_string( stream, size ):
return stream.get_partial_data( size )[1].get_string_from_ascii( )
# -----------------------------------------------------------------------------
# 書き込み: Writer
"""
@param smf SMF structure
@return PoolByteArray
"""
func write( smf ):
var stream = StreamPeerBuffer.new( )
stream.big_endian = true
stream.put_utf8_string( "MThd".to_ascii( ) )
stream.put_u32( 6 )
stream.put_u16( smf.format_type )
stream.put_u16( len( smf.tracks ) )
stream.put_u16( smf.timebase )
for t in smf.tracks:
self._write_track( stream, t )
return stream.get_partial_data( stream.get_available_bytes( ) )[1]
"""
"""
class TrackEventSorter:
static func sort( a, b ):
if a.time < b.time:
return true
return false
"""
@param stream
@param i
"""
func _write_variable_int( stream, i ):
while true:
var v = i & 0x7f
i >>= 7
if i != 0:
stream.put_u8( v | 0x80 )
else:
stream.put_u8( v )
break
"""
@param stream
@param track
"""
func _write_track( stream, track ):
var events = track.events.duplicate( )
events.sort_custom( TrackEventSorter, "sort" )
var buf = StreamPeerBuffer.new( )
buf.big_endian = true
var time = 0
for e in events:
self._write_variable_int( buf, e.time - time )
time = e.time
match e.type:
MIDIEventType.note_off:
buf.put_u8( 0x80 | e.channel_number )
buf.put_u8( e.note )
buf.put_u8( e.velocity )
MIDIEventType.note_on:
buf.put_u8( 0x90 | e.channel_number )
buf.put_u8( e.note )
buf.put_u8( e.velocity )
MIDIEventType.polyphonic_key_pressure:
buf.put_u8( 0xA0 | e.channel_number )
buf.put_u8( e.note )
buf.put_u8( e.value )
MIDIEventType.control_change:
buf.put_u8( 0xB0 | e.channel_number )
buf.put_u8( e.number )
buf.put_u8( e.value )
MIDIEventType.program_change:
buf.put_u8( 0xC0 | e.channel_number )
buf.put_u8( e.number )
MIDIEventType.channel_pressure:
buf.put_u8( 0xD0 | e.channel_number )
buf.put_u8( e.value )
MIDIEventType.pitch_bend:
buf.put_u8( 0xE0 | e.channel_number )
buf.put_u8( e.value & 0x7f )
buf.put_u8( ( e.value >> 7 ) & 0x7f )
MIDIEventType.system_event:
self._write_system_event( buf, e )
var track_size = buf.get_available_bytes( )
stream.put_utf8_string( "MTrk".to_ascii( ) )
stream.put_u32( track_size )
stream.put_data( buf.get_partial_data( track_size )[1] )
"""
@param stream
@param event
"""
func _write_system_event( stream, event ):
match event.type:
MIDISystemEventType.sys_ex:
stream.put_u8( 0xF0 )
self._write_variable_int( stream, len( event.data ) )
stream.put_data( event.data )
MIDISystemEventType.divided_sys_ex:
stream.put_u8( 0xF7 )
self._write_variable_int( stream, len( event.data ) )
stream.put_data( event.data )
MIDISystemEventType.text_event:
stream.put_u8( 0xFF )
stream.put_u8( 0x01 )
self._write_variable_int( stream, len( event.text ) )
stream.put_data( event.text.to_ascii( ) )
MIDISystemEventType.copyright:
stream.put_u8( 0xFF )
stream.put_u8( 0x02 )
self._write_variable_int( stream, len( event.text ) )
stream.put_data( event.text.to_ascii( ) )
MIDISystemEventType.track_name:
stream.put_u8( 0xFF )
stream.put_u8( 0x03 )
self._write_variable_int( stream, len( event.text ) )
stream.put_data( event.text.to_ascii( ) )
MIDISystemEventType.instrument_name:
stream.put_u8( 0xFF )
stream.put_u8( 0x04 )
self._write_variable_int( stream, len( event.text ) )
stream.put_data( event.text.to_ascii( ) )
MIDISystemEventType.lyric:
stream.put_u8( 0xFF )
stream.put_u8( 0x05 )
self._write_variable_int( stream, len( event.text ) )
stream.put_data( event.text.to_ascii( ) )
MIDISystemEventType.marker:
stream.put_u8( 0xFF )
stream.put_u8( 0x06 )
self._write_variable_int( stream, len( event.text ) )
stream.put_data( event.text.to_ascii( ) )
MIDISystemEventType.cue_point:
stream.put_u8( 0xFF )
stream.put_u8( 0x07 )
self._write_variable_int( stream, len( event.text ) )
stream.put_data( event.text.to_ascii( ) )
MIDISystemEventType.midi_channel_prefix:
stream.put_u8( 0xFF )
stream.put_u8( 0x20 )
stream.put_u8( 0x01 )
stream.put_u8( event.prefix )
MIDISystemEventType.end_of_track:
stream.put_u8( 0xFF )
stream.put_u8( 0x2F )
stream.put_u8( 0x00 )
MIDISystemEventType.set_tempo:
stream.put_u8( 0xFF )
stream.put_u8( 0x51 )
stream.put_u8( 0x03 )
stream.put_u8( ( event.bpm >> 16 ) & 0xFF )
stream.put_u8( ( event.bpm >> 8 ) & 0xFF )
stream.put_u8( event.bpm & 0xFF )
MIDISystemEventType.smpte_offset:
stream.put_u8( 0xFF )
stream.put_u8( 0x54 )
stream.put_u8( 0x05 )
stream.put_u8( event.hr )
stream.put_u8( event.mm )
stream.put_u8( event.se )
stream.put_u8( event.fr )
stream.put_u8( event.ff )
MIDISystemEventType.beat:
stream.put_u8( 0xFF )
stream.put_u8( 0x58 )
stream.put_u8( 0x04 )
stream.put_u8( event.numerator )
stream.put_u8( event.denominator )
stream.put_u8( event.clock )
stream.put_u8( event.beat32 )
MIDISystemEventType.key:
stream.put_u8( 0xFF )
stream.put_u8( 0x59 )
stream.put_u8( 0x02 )
stream.put_u8( event.sf )
stream.put_u8( 1 if event.minor else 0 )
MIDISystemEventType.unknown:
stream.put_u8( 0xFF )
stream.put_u8( event.meta_type )
stream.put_u8( len( event.data ) )
stream.put_data( event.data )

493
midi/SoundFont.gd Normal file
View File

@ -0,0 +1,493 @@
"""
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

BIN
midi/Tetris - Song A.mid Normal file

Binary file not shown.

BIN
midi/TimGM6mb.sf2 Normal file

Binary file not shown.

View File

@ -52,6 +52,6 @@ rotate_clockwise=[ Object(InputEventJoypadButton,"resource_local_to_scene":false
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":88,"unicode":0,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":88,"unicode":0,"echo":false,"script":null)
] ]
rotate_counterclockwise=[ Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) rotate_counterclockwise=[ Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777221,"unicode":0,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777224,"unicode":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":90,"unicode":0,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":90,"unicode":0,"echo":false,"script":null)
] ]