BGM Render Shader mostly working
This commit is contained in:
parent
302eaac7ac
commit
a3231c1ba9
|
@ -14,14 +14,14 @@ bytelength_sfx_brr_data 0x041E3F u16 Used by the memcpy routine that copies th
|
|||
sfx_brr_data 0x041E41 Use the below SPC pointers
|
||||
bytelength_sfx_brr_pointers 0x041F4F u16 Used by the memcpy routine that copies the below data to the SPC (0x0020 = 32 bytes)
|
||||
sfx_brr_pointers 0x041F51 8 of 2 of u16 SPC memory addresses not ROM. Start address followed by loop address.
|
||||
sfx_adsrs 0x041F71 8 of u16
|
||||
sfx_adsrs 0x041F71 8 of 4 of u4
|
||||
sfx_samplerates 0x041F83 8 of u16
|
||||
sfx_data 0x041F95 Contains SPC pointers and tracks
|
||||
bgm_song_pointers 0x043B97 72 of u24
|
||||
bgm_instrument_brr_pointers 0x043C6F 35 of u24
|
||||
bgm_instrument_loop_starts 0x043CD8 35 of u16
|
||||
bgm_instrument_samplerates 0x043D1E 35 of u16
|
||||
bgm_instrument_adsrs 0x043D64 35 of u16
|
||||
bgm_instrument_adsrs 0x043D64 35 of 4 of u4
|
||||
bgm_instrument_indices 0x043DAA 72 of 16 of u16 length 0x900
|
||||
|
||||
worldmap_compressed_tilesets 0x070000 tilesets 0 up to 0x434
|
||||
|
|
|
|
@ -0,0 +1,70 @@
|
|||
Ahead on our way
|
||||
The Fierce Battle
|
||||
A Presentiment
|
||||
Go Go Boko!
|
||||
Pirates Ahoy
|
||||
Tenderness in the Air
|
||||
Fate in Haze
|
||||
Moogle theme
|
||||
Prelude/Crystal Room
|
||||
The Last Battle
|
||||
Requiem
|
||||
Nostalgia
|
||||
Cursed Earths
|
||||
Lenna's Theme
|
||||
Victory's Fanfare
|
||||
Deception
|
||||
The Day Will Come
|
||||
[nothing]
|
||||
ExDeath's Castle
|
||||
My Home, Sweet Home
|
||||
Waltz Suomi
|
||||
Sealed Away
|
||||
The Four Warriors of Dawn
|
||||
Danger
|
||||
The Fire Powered Ship
|
||||
As I Feel, You Feel
|
||||
Mambo de Chocobo!
|
||||
Music Box
|
||||
Intension of the Earth
|
||||
The Dragon Spreads its Wings
|
||||
Beyond the Deep Blue Sea
|
||||
The Prelude of Empty Skies
|
||||
Searching the Light
|
||||
Harvest
|
||||
Battle with Gilgamesh
|
||||
Four Valiant Hearts
|
||||
The Book of Sealings
|
||||
What?
|
||||
Hurry! Hurry!
|
||||
Unknown Lands
|
||||
The Airship
|
||||
Fanfare 1
|
||||
Fanfare 2
|
||||
The Battle
|
||||
Walking the Snowy Mountains
|
||||
The Evil Lord Exdeath
|
||||
The Castle of Dawn
|
||||
I'm a Dancer
|
||||
Reminiscence
|
||||
Run!
|
||||
The Ancient Library
|
||||
Royal Palace
|
||||
Good Night!
|
||||
Piano Lesson 1
|
||||
Piano Lesson 2
|
||||
Piano Lesson 3
|
||||
Piano Lesson 4
|
||||
Piano Lesson 5
|
||||
Piano Lesson 6
|
||||
Piano Lesson 7
|
||||
Piano Lesson 8
|
||||
Musica Machina
|
||||
[meteor falling?]
|
||||
The Land Unknown
|
||||
The Decisive Battle
|
||||
The Silent Beyond
|
||||
Dear Friends
|
||||
Final Fantasy
|
||||
A New Origin
|
||||
[chirping sound]
|
|
@ -42,6 +42,8 @@ const POST_ROM_MENUS = [
|
|||
Menu.DEBUG_WORLD_MAP_BLOCKS,
|
||||
]
|
||||
|
||||
const ERROR_CODE_STRINGS = PoolStringArray(['OK', 'FAILED', 'ERR_UNAVAILABLE', 'ERR_UNCONFIGURED', 'ERR_UNAUTHORIZED', 'ERR_PARAMETER_RANGE_ERROR', 'ERR_OUT_OF_MEMORY', 'ERR_FILE_NOT_FOUND', 'ERR_FILE_BAD_DRIVE', 'ERR_FILE_BAD_PATH', 'ERR_FILE_NO_PERMISSION', 'ERR_FILE_ALREADY_IN_USE', 'ERR_FILE_CANT_OPEN', 'ERR_FILE_CANT_WRITE', 'ERR_FILE_CANT_READ', 'ERR_FILE_UNRECOGNIZED', 'ERR_FILE_CORRUPT', 'ERR_FILE_MISSING_DEPENDENCIES', 'ERR_FILE_EOF', 'ERR_CANT_OPEN', 'ERR_CANT_CREATE', 'ERR_QUERY_FAILED', 'ERR_ALREADY_IN_USE', 'ERR_LOCKED', 'ERR_TIMEOUT', 'ERR_CANT_CONNECT', 'ERR_CANT_RESOLVE', 'ERR_CONNECTION_ERROR', 'ERR_CANT_ACQUIRE_RESOURCE', 'ERR_CANT_FORK', 'ERR_INVALID_DATA', 'ERR_INVALID_PARAMETER', 'ERR_ALREADY_EXISTS', 'ERR_DOES_NOT_EXIST', 'ERR_DATABASE_CANT_READ', 'ERR_DATABASE_CANT_WRITE', 'ERR_COMPILATION_FAILED', 'ERR_METHOD_NOT_FOUND', 'ERR_LINK_FAILED', 'ERR_SCRIPT_FAILED', 'ERR_CYCLIC_LINK', 'ERR_INVALID_DECLARATION', 'ERR_DUPLICATE_SYMBOL', 'ERR_PARSE_ERROR', 'ERR_BUSY', 'ERR_SKIP', 'ERR_HELP', 'ERR_BUG', 'ERR_PRINTER_ON_FIRE'])
|
||||
|
||||
const FOLDER_ICON := preload('res://theme/icons/file_folder.tres')
|
||||
const ALLOWED_EXTS := PoolStringArray(['bin', 'iso', 'sfc', 'smc', 'srm', 'gba'])
|
||||
const CD_EXTS := PoolStringArray(['bin', 'iso']) # If you have a weird disc image format, you can mount it yourself, leave me out of it
|
||||
|
|
|
@ -341,6 +341,7 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
|
|||
# the strategy will be to preprocess each channel in a global-state-agnostic way,
|
||||
# then once all the global tracks are known, as well as the longest unlooped length,
|
||||
# do a second pass to generate the final events
|
||||
var instrument_adsrs = RomLoader.snes_data.bgm_instrument_adsrs # TODO: UNHARDCODE THIS
|
||||
var all_note_events = []
|
||||
|
||||
var curve_master_volume := TrackCurve.new(100.0/255.0) # [0.0, 1.0] for now
|
||||
|
@ -465,6 +466,19 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
|
|||
var event_idx = event[1]-0x20
|
||||
if event_idx >= 0:
|
||||
current_instrument = inst_map[event_idx] - 1
|
||||
if current_instrument < len(instrument_adsrs) and current_instrument > 0:
|
||||
var adsr = instrument_adsrs[current_instrument]
|
||||
current_adsr_attack = adsr[2]
|
||||
current_adsr_decay = adsr[3]
|
||||
current_adsr_sustain = adsr[0]
|
||||
current_adsr_release = adsr[1]
|
||||
EventType.ADSR_DEFAULT: # TODO - Investigate actual scaling and order
|
||||
if current_instrument < len(instrument_adsrs) and current_instrument > 0:
|
||||
var adsr = instrument_adsrs[current_instrument]
|
||||
current_adsr_attack = adsr[2]
|
||||
current_adsr_decay = adsr[3]
|
||||
current_adsr_sustain = adsr[0]
|
||||
current_adsr_release = adsr[1]
|
||||
EventType.ADSR_ATTACK:
|
||||
current_adsr_attack = event[1]
|
||||
EventType.ADSR_DECAY:
|
||||
|
@ -473,11 +487,6 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
|
|||
current_adsr_sustain = event[1]
|
||||
EventType.ADSR_RELEASE:
|
||||
current_adsr_release = event[1]
|
||||
EventType.ADSR_DEFAULT: # TODO - grab instrument envelope
|
||||
current_adsr_attack = 0
|
||||
current_adsr_decay = 0
|
||||
current_adsr_sustain = 0
|
||||
current_adsr_release = 0
|
||||
EventType.VIBRATO_ON:
|
||||
self.channel_vibrato_delay[channel] = event[1]
|
||||
self.channel_vibrato_rate[channel] = event[2]
|
||||
|
@ -535,6 +544,7 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
|
|||
var channel_loop_p_lengths = PoolIntArray()
|
||||
var longest_channel_idx = 0
|
||||
var longest_channel_p_end = 0
|
||||
var highest_channel_p_return = -1
|
||||
for channel in self.num_tracks:
|
||||
if all_note_events[channel].empty():
|
||||
channel_loop_p_returns.append(-1)
|
||||
|
@ -545,6 +555,8 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
|
|||
# Ends on infinite loop
|
||||
channel_loop_p_returns.append(p_end)
|
||||
channel_loop_p_lengths.append(note_event.p_start - p_end)
|
||||
if p_end > highest_channel_p_return:
|
||||
highest_channel_p_return = p_end
|
||||
p_end = note_event.p_start
|
||||
else:
|
||||
channel_loop_p_returns.append(-1)
|
||||
|
@ -603,4 +615,9 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
|
|||
midi_events_bytes3.put_32(0)
|
||||
midi_events_bytes_adsr.put_32(0)
|
||||
data += midi_events_bytes_t_start.data_array + midi_events_bytes_t_end.data_array + midi_events_bytes3.data_array + midi_events_bytes_adsr.data_array
|
||||
return [data, target_time_length]
|
||||
var smp_loop_start = -1
|
||||
var smp_loop_end = -1
|
||||
if highest_channel_p_return > 0:
|
||||
smp_loop_start = curve_master_tempo.get_integral(highest_channel_p_return + 100) * 32000
|
||||
smp_loop_end = curve_master_tempo.get_integral(longest_channel_p_end + 100) * 32000
|
||||
return [data, target_time_length, [smp_loop_start, smp_loop_end]]
|
||||
|
|
|
@ -174,16 +174,14 @@ func load_samples(snes_data: Dictionary, buffer: StreamPeerBuffer):
|
|||
instrument_samples_HACK_EXTENDED_LOOPS.append(HACK_EXTEND_LOOP_SAMPLE(samp))
|
||||
# print('Instrument %02X has mix_rate %d Hz and %d samples'%[i, samp.mix_rate, len(samp.data)/2])
|
||||
emit_signal('audio_inst_sample_loaded', i)
|
||||
samp.save_to_wav('output/instrument%02d(%dHz)(loop from %d to %d of %d).wav' % [i, samp.mix_rate, samp.loop_begin, samp.loop_end, len(samp.data)/2])
|
||||
# samp.save_to_wav('output/instrument%02d(%dHz)(loop from %d to %d of %d).wav' % [i, samp.mix_rate, samp.loop_begin, samp.loop_end, len(samp.data)/2])
|
||||
|
||||
|
||||
# We start the texture with a bunch of same-size headers
|
||||
# uint16 sample_start // The true start, after the prepended 3 frames of silence
|
||||
# int32 sample_start // The true start, after the prepended 3 frames of silence
|
||||
# uint16 sample_length // 3 frames after the true end, because of how we loop
|
||||
# uint16 sample_loop_begin // 3 frames after the true loop point
|
||||
# uint16 mixrate
|
||||
# 2*uint8 AD of ADSR ([0.0, 1.0] is fine)
|
||||
# 2*uint8 SR of ADSR ([0.0, 1.0] is fine)
|
||||
var samples_tex: ImageTexture
|
||||
const TEX_WIDTH := 2048
|
||||
const FILTER_PAD := 32
|
||||
|
@ -236,15 +234,7 @@ func samples_to_texture():
|
|||
datasamp.data = data
|
||||
datasamp.mix_rate = 32000
|
||||
datasamp.format = AudioStreamSample.FORMAT_16_BITS
|
||||
datasamp.save_to_wav('output/texture_inst_data.wav')
|
||||
# # Now calculate wrapping and rowwise padding for the combined array
|
||||
# for row in TEX_WIDTH:
|
||||
# var row_end: int = (row + 1) * TEX_WIDTH * 2 # Remember: 8bit array, 16bit values
|
||||
# if len(data)/2 > row_end:
|
||||
# # [... a b c] + [a b c] + [a b c ...]
|
||||
# data = data.subarray(0, row_end-1) + data.subarray(row_end-FILTER_PAD*2, row_end-1) + data.subarray(row_end-FILTER_PAD*2, -1)
|
||||
# else:
|
||||
# break
|
||||
# datasamp.save_to_wav('output/texture_inst_data.wav')
|
||||
var needed_rows := (len(data)/2)/float(TEX_WIDTH)
|
||||
var rows := int(pow(2, ceil(log(needed_rows) / log(2))))
|
||||
if rows > TEX_WIDTH:
|
||||
|
@ -265,14 +255,10 @@ func samples_to_texture():
|
|||
|
||||
|
||||
var player := AudioStreamPlayer.new() # Make one for each channel, later
|
||||
var HACK_EXTEND_LOOP_SAMPLE_playback: bool = true
|
||||
func play_sample(id: int, pitch_scale: float = 1.0):
|
||||
print('Playing inst sample #%02X' % id)
|
||||
player.pitch_scale = pitch_scale
|
||||
if HACK_EXTEND_LOOP_SAMPLE_playback:
|
||||
player.stream = instrument_samples_HACK_EXTENDED_LOOPS[id]
|
||||
else:
|
||||
player.stream = instrument_samples[id]
|
||||
player.stream = instrument_samples_HACK_EXTENDED_LOOPS[id]
|
||||
player.play(PLAY_START/pitch_scale)
|
||||
|
||||
func play_sfx(id: int):
|
||||
|
|
|
@ -186,7 +186,7 @@ vec4 render_song(int smp) {
|
|||
// ====================At some point I'll look back into packing floats====================
|
||||
// TBD = note_event_supplement.zw; - tremolo/vibrato/noise/pan_lfo/pitchbend/echo remain
|
||||
// ====================At some point I'll look back into packing floats====================
|
||||
float attack = adsr.x*65535.0 + 1.0; // TODO: work out effective resolution for this
|
||||
float attack = 1.0 + adsr.x*255.0; //65535.0 + 1.0; // TODO: work out effective resolution for this
|
||||
int smp_attack = int(attack) * 2; // Max value is 131072 samples = 4.096 seconds
|
||||
|
||||
// For now, just branch this
|
||||
|
|
|
@ -8,23 +8,23 @@ const OUTPUT_WIDTH := 4096
|
|||
const QUAD_COLOR := PoolColorArray([Color.white, Color.white, Color.white, Color.white])
|
||||
var viewport: Viewport
|
||||
var render_queue: Array # of Images
|
||||
var result_queue: Array # of PoolByteArrays
|
||||
var result_queue: Array # of [String, PoolByteArray]
|
||||
var current_tex: ImageTexture # Needed to prevent GC before draw
|
||||
var waiting_for_viewport: int
|
||||
var waiting_for_viewport: Array
|
||||
var done_first_draw: bool
|
||||
|
||||
func _ready() -> void:
|
||||
self.viewport = get_parent()
|
||||
self.render_queue = []
|
||||
self.result_queue = []
|
||||
self.waiting_for_viewport = 0
|
||||
self.waiting_for_viewport = []
|
||||
self.done_first_draw = false
|
||||
self.current_tex = ImageTexture.new()
|
||||
|
||||
func push_image(img: Image, uv_rows: int = 4096) -> void:
|
||||
self.render_queue.append([img, uv_rows])
|
||||
func push_image(img: Image, uv_rows: int = 4096, desc: String = '') -> void:
|
||||
self.render_queue.append([img, uv_rows, desc])
|
||||
|
||||
func push_bytes(data: PoolByteArray, uv_rows: int = 4096) -> void:
|
||||
func push_bytes(data: PoolByteArray, uv_rows: int = 4096, desc: String = '') -> void:
|
||||
# print(data.subarray(0, 15))
|
||||
var rows = int(pow(2, ceil(log((len(data)/INPUT_BYTES_PER_TEXEL) / INPUT_TEX_WIDTH)/log(2))))
|
||||
var target_length = rows * INPUT_BYTES_PER_TEXEL * INPUT_FORMAT
|
||||
|
@ -32,7 +32,7 @@ func push_bytes(data: PoolByteArray, uv_rows: int = 4096) -> void:
|
|||
data.append(0)
|
||||
var image := Image.new()
|
||||
image.create_from_data(INPUT_TEX_WIDTH, rows, false, INPUT_FORMAT, data)
|
||||
self.render_queue.append([image, uv_rows])
|
||||
self.render_queue.append([image, uv_rows, desc])
|
||||
|
||||
func _process(_delta) -> void:
|
||||
update()
|
||||
|
@ -52,27 +52,29 @@ func _draw() -> void:
|
|||
return
|
||||
|
||||
# Draw the next ImageTexture
|
||||
var image_and_uv_rows = self.render_queue.pop_front()
|
||||
self.current_tex.create_from_image(image_and_uv_rows[0], 0)
|
||||
var image_and_uv_rows_and_desc = self.render_queue.pop_front()
|
||||
self.current_tex.create_from_image(image_and_uv_rows_and_desc[0], 0)
|
||||
self.material.set_shader_param('midi_events', self.current_tex)
|
||||
self.material.set_shader_param('midi_events_size', self.current_tex.get_size())
|
||||
var uv_rows: int = image_and_uv_rows[1]
|
||||
var uv_rows: int = image_and_uv_rows_and_desc[1]
|
||||
var uv_rows_inv: int = 4096 - uv_rows
|
||||
var uv_v: float = uv_rows / float(OUTPUT_WIDTH)
|
||||
var points := PoolVector2Array([Vector2(0, uv_rows_inv), Vector2(OUTPUT_WIDTH, uv_rows_inv), Vector2(OUTPUT_WIDTH, OUTPUT_WIDTH), Vector2(0, OUTPUT_WIDTH)])
|
||||
var uvs := PoolVector2Array([Vector2(0, 1-uv_v), Vector2(1, 1-uv_v), Vector2(1, 1), Vector2(0, 1)])
|
||||
draw_primitive(points, QUAD_COLOR, uvs, self.current_tex)
|
||||
self.waiting_for_viewport = uv_rows # Grab the result next draw
|
||||
self.waiting_for_viewport = [uv_rows, image_and_uv_rows_and_desc[2]] # Grab the result next draw
|
||||
|
||||
func get_result() -> void:
|
||||
var result_rows: int = waiting_for_viewport[0]
|
||||
var result_desc: String = waiting_for_viewport[1]
|
||||
var result_texture := self.viewport.get_texture()
|
||||
var result_image := result_texture.get_data()
|
||||
var result_bytes := result_image.get_data()
|
||||
var result_byte_count := waiting_for_viewport * OUTPUT_WIDTH * OUTPUT_BYTES_PER_TEXEL
|
||||
var result_byte_count := result_rows * OUTPUT_WIDTH * OUTPUT_BYTES_PER_TEXEL
|
||||
result_bytes.resize(result_byte_count)
|
||||
|
||||
self.result_queue.append(result_bytes)
|
||||
self.waiting_for_viewport = 0
|
||||
self.result_queue.append([result_desc, result_bytes])
|
||||
self.waiting_for_viewport = []
|
||||
|
||||
# # Debugging: compare a sequence of all the possible 16bit integers
|
||||
# print_debug('result_image format is %d and has size'%result_image.get_format(), result_image.get_size(), result_bytes.subarray(0, 11))
|
||||
|
|
|
@ -2,8 +2,20 @@ extends Node2D
|
|||
#warning-ignore-all:return_value_discarded
|
||||
const MusicPlayer := preload('res://scripts/MusicPlayer.gd')
|
||||
var MusicLoader := preload('res://scripts/loaders/snes/music_ff5.gd').new()
|
||||
onready var bgm_titles := Common.load_glyph_table('res://data/5/bgm_titles.txt')
|
||||
onready var audio_renderer := $'%audio_renderer'
|
||||
onready var audio_player := $audio_player
|
||||
var prerendered_bgms := {}
|
||||
var prerendered_bgm_start_and_end_loops := {}
|
||||
var inst_buttons = []
|
||||
var sfx_buttons = []
|
||||
var initialized_instrument_texture := false
|
||||
var queued_bgm_playback := ''
|
||||
|
||||
const NUM_CHANNELS := 8
|
||||
var music_player = null
|
||||
var inst_sample_map := {}
|
||||
var bgm_tracksets := {}
|
||||
|
||||
func _create_sfx_buttons():
|
||||
var disable_btn := !SoundLoader.has_loaded_audio_samples
|
||||
|
@ -48,11 +60,6 @@ func _enable_inst_button(id: int):
|
|||
for i in id+1:
|
||||
inst_buttons[i].disabled = false
|
||||
|
||||
const NUM_CHANNELS := 8
|
||||
var music_player = null
|
||||
var inst_sample_map := {}
|
||||
var bgm_tracksets := {}
|
||||
|
||||
func evaluate_bgm(id: int):
|
||||
var buffer: StreamPeerBuffer = RomLoader.snes_buffer.duplicate()
|
||||
var bgm_song_ptr: int = RomLoader.snes_data.bgm_song_pointers[id] & 0x3FFFFF
|
||||
|
@ -77,63 +84,145 @@ func evaluate_bgm(id: int):
|
|||
tracks.append(MusicLoader.unroll_track(buffer.duplicate(), bgm_song_ptr, track_ptr, end_ptr, '%02d:%02d'%[id, i]))
|
||||
bgm_tracksets[id] = tracks
|
||||
|
||||
func play_bgm(id: int) -> void:
|
||||
var inst_indices = RomLoader.snes_data.bgm_instrument_indices[id]
|
||||
for i in 16:
|
||||
var inst_idx: int = inst_indices[i]-1
|
||||
if inst_idx < 0:
|
||||
self.inst_sample_map[i + 0x20] = null
|
||||
else:
|
||||
if SoundLoader.HACK_EXTEND_LOOP_SAMPLE_playback:
|
||||
self.inst_sample_map[i + 0x20] = SoundLoader.instrument_samples_HACK_EXTENDED_LOOPS[inst_idx]
|
||||
func play_bgm(id: int, live: bool) -> void:
|
||||
self._stop_all()
|
||||
if live:
|
||||
var inst_indices = RomLoader.snes_data.bgm_instrument_indices[id]
|
||||
for i in 16:
|
||||
var inst_idx: int = inst_indices[i]-1
|
||||
if inst_idx < 0:
|
||||
self.inst_sample_map[i + 0x20] = null
|
||||
else:
|
||||
self.inst_sample_map[i + 0x20] = SoundLoader.instrument_samples[inst_idx]
|
||||
if self.music_player:
|
||||
remove_child(music_player)
|
||||
self.music_player = MusicPlayer.new(bgm_tracksets[id], self.inst_sample_map)
|
||||
add_child(self.music_player)
|
||||
self.music_player.is_playing = true
|
||||
self.inst_sample_map[i + 0x20] = SoundLoader.instrument_samples_HACK_EXTENDED_LOOPS[inst_idx]
|
||||
if self.music_player:
|
||||
remove_child(music_player)
|
||||
self.music_player = MusicPlayer.new(bgm_tracksets[id], self.inst_sample_map)
|
||||
add_child(self.music_player)
|
||||
self.music_player.is_playing = true
|
||||
else:
|
||||
# Play prerendered
|
||||
var bgm_key = 'BGM%02d'%id
|
||||
if bgm_key in self.prerendered_bgms:
|
||||
self.audio_player.stream = self.prerendered_bgms[bgm_key]
|
||||
self.audio_player.play()
|
||||
else:
|
||||
self.queue_prerender_bgm(id)
|
||||
self.queued_bgm_playback = bgm_key
|
||||
print('Playing BGM%02d' % id)
|
||||
|
||||
func _play_bgm() -> void:
|
||||
self.play_bgm($sb_bgm.value)
|
||||
func _play_bgm_live() -> void:
|
||||
self.play_bgm($sb_bgm.value, true)
|
||||
|
||||
func _play_bgm_prerendered() -> void:
|
||||
self.play_bgm($sb_bgm.value, false)
|
||||
|
||||
|
||||
func _create_bgm_playback() -> void:
|
||||
$sb_bgm.max_value = SoundLoader.BGM_NUM
|
||||
$btn_bgm.connect('pressed', self, '_play_bgm')
|
||||
$sb_bgm.connect('value_changed', self, '_update_bgm_label')
|
||||
self._update_bgm_label()
|
||||
$btn_bgm_live.connect('pressed', self, '_play_bgm_live')
|
||||
$btn_bgm_prerendered.connect('pressed', self, '_play_bgm_prerendered')
|
||||
$btn_render.connect('pressed', self, 'render_all_bgm')
|
||||
for i in SoundLoader.SFX_NUM:
|
||||
self.inst_sample_map[i] = SoundLoader.sfx_samples[i]
|
||||
for i in SoundLoader.BGM_NUM:
|
||||
evaluate_bgm(i)
|
||||
|
||||
|
||||
func _stop_all() -> void:
|
||||
if self.music_player:
|
||||
self.music_player.queue_free()
|
||||
self.music_player = null
|
||||
SoundLoader.player.stop()
|
||||
self.audio_player.stop()
|
||||
|
||||
|
||||
func _update_bgm_label(id = 0) -> void:
|
||||
if id < len(bgm_titles):
|
||||
$lbl_bgm_title.text = bgm_titles[id]
|
||||
else:
|
||||
$lbl_bgm_title.text = ''
|
||||
|
||||
func _update_loop_hack_status(enabled: bool) -> void:
|
||||
SoundLoader.HACK_EXTEND_LOOP_SAMPLE_playback = enabled
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
self._create_sfx_buttons()
|
||||
self._create_bgm_playback()
|
||||
$btn_stop.connect('pressed', self, '_stop_all')
|
||||
$btn_hack_loop_extension.connect('toggled', self, '_update_loop_hack_status')
|
||||
$btn_hack_loop_extension.text += ' (%dms)'%SoundLoader.HACK_EXTEND_LOOP_SAMPLE_EXTRA_MS
|
||||
for i in len(RomLoader.snes_data.bgm_song_pointers):
|
||||
var pointer = RomLoader.snes_data.bgm_song_pointers[i]
|
||||
# print('BGM 0x%02X (%02d) at 0x%06X' % [i, i, pointer])
|
||||
self.test_rendering()
|
||||
|
||||
onready var audio_renderer := $'%audio_renderer'
|
||||
onready var load_start_tick := Time.get_ticks_msec()
|
||||
func test_rendering() -> void:
|
||||
|
||||
var load_start_tick: int
|
||||
func get_ms(restart: bool = false) -> int:
|
||||
if restart or not self.load_start_tick:
|
||||
self.load_start_tick = Time.get_ticks_msec()
|
||||
return 0
|
||||
return Time.get_ticks_msec() - self.load_start_tick
|
||||
|
||||
|
||||
func initialize_instrument_texture() -> void:
|
||||
get_ms(true)
|
||||
SoundLoader.samples_to_texture()
|
||||
audio_renderer.material.set_shader_param('instrument_samples', SoundLoader.samples_tex)
|
||||
audio_renderer.material.set_shader_param('instrument_samples_size', SoundLoader.samples_tex.get_size())
|
||||
self.initialized_instrument_texture = true
|
||||
print('@%dms - Initialized instrument samples texture' % get_ms())
|
||||
|
||||
|
||||
func queue_prerender_bgm(bgm_id: int) -> void:
|
||||
if not self.initialized_instrument_texture:
|
||||
self.initialize_instrument_texture()
|
||||
var mp = MusicPlayer.new(bgm_tracksets[bgm_id], self.inst_sample_map)
|
||||
var data_and_target_time_and_loops = mp.render_channels(0, 540, RomLoader.snes_data.bgm_instrument_indices[bgm_id])
|
||||
var data = data_and_target_time_and_loops[0]
|
||||
var target_time = data_and_target_time_and_loops[1]
|
||||
var target_samples = target_time * 32000
|
||||
var target_rows = ceil(target_samples/4096.0)
|
||||
var bgm_key := 'BGM%02d'%bgm_id
|
||||
audio_renderer.push_bytes(data, target_rows, bgm_key)
|
||||
self.prerendered_bgm_start_and_end_loops[bgm_key] = data_and_target_time_and_loops[2]
|
||||
|
||||
|
||||
|
||||
func render_all_bgm(bgms_to_render: int = 64) -> void:
|
||||
self.initialize_instrument_texture()
|
||||
for bgm_id in bgms_to_render:
|
||||
self.queue_prerender_bgm(bgm_id)
|
||||
if bgm_id % 10 == 9 and bgm_id < bgms_to_render-1:
|
||||
print('@%dms - Processed %d/%d bgm tracks' % [get_ms(), bgm_id+1, bgms_to_render])
|
||||
print('@%dms - Processed %d bgm tracks and sent to gpu for rendering' % [get_ms(), bgms_to_render])
|
||||
|
||||
|
||||
const save_prerendered_audio := false
|
||||
func _get_prerendered_audio():
|
||||
audio_renderer.get_result()
|
||||
var result = audio_renderer.result_queue.pop_back()
|
||||
var desc = result[0]
|
||||
var rendered_audio := AudioStreamSample.new()
|
||||
rendered_audio.data = result[1]
|
||||
rendered_audio.stereo = true
|
||||
rendered_audio.mix_rate = 32000
|
||||
rendered_audio.format = AudioStreamSample.FORMAT_16_BITS
|
||||
if prerendered_bgm_start_and_end_loops[desc][0] >= 0:
|
||||
rendered_audio.loop_begin = int(round(prerendered_bgm_start_and_end_loops[desc][0]))
|
||||
rendered_audio.loop_end = int(round(prerendered_bgm_start_and_end_loops[desc][1]))
|
||||
rendered_audio.loop_mode = AudioStreamSample.LOOP_FORWARD
|
||||
self.prerendered_bgms[desc] = rendered_audio
|
||||
if save_prerendered_audio:
|
||||
var error = rendered_audio.save_to_wav('output/rendered_%s.wav'%desc)
|
||||
print('@%dms - Saved render of %s (error code %s)' % [get_ms(), desc, globals.ERROR_CODE_STRINGS[error]])
|
||||
else:
|
||||
print('@%dms - Rendered %s without saving' % [get_ms(), desc])
|
||||
if self.queued_bgm_playback == desc:
|
||||
self.audio_player.stream = rendered_audio
|
||||
self.audio_player.play()
|
||||
self.queued_bgm_playback = ''
|
||||
|
||||
|
||||
func get_shader_test_pattern() -> PoolByteArray:
|
||||
var midi_events_bytes := StreamPeerBuffer.new()
|
||||
var midi_events_bytes2 := StreamPeerBuffer.new()
|
||||
var midi_events_bytes3 := StreamPeerBuffer.new()
|
||||
|
@ -149,43 +238,12 @@ func test_rendering() -> void:
|
|||
midi_events_bytes3.put_u8(0) # pan
|
||||
# midi_events_bytes3.put_u8(i%256) # pan
|
||||
midi_events_bytes4.put_32(0) # ADSR
|
||||
var channel_data = midi_events_bytes.data_array + midi_events_bytes2.data_array + midi_events_bytes3.data_array + midi_events_bytes4.data_array
|
||||
for bgm_id in 64:
|
||||
var mp = MusicPlayer.new(bgm_tracksets[bgm_id], self.inst_sample_map)
|
||||
var data_and_target_time = mp.render_channels(0, 540, RomLoader.snes_data.bgm_instrument_indices[bgm_id])
|
||||
channel_data = data_and_target_time[0]
|
||||
var target_time = data_and_target_time[1]
|
||||
var target_samples = target_time * 32000
|
||||
var target_rows = ceil(target_samples/4096.0)
|
||||
audio_renderer.push_bytes(channel_data, target_rows) # + channel_data + channel_data + channel_data + channel_data + channel_data + channel_data + channel_data)
|
||||
return midi_events_bytes.data_array + midi_events_bytes2.data_array + midi_events_bytes3.data_array + midi_events_bytes4.data_array
|
||||
|
||||
# var test_payload := PoolByteArray()
|
||||
# test_payload.resize(4096*4096*2)
|
||||
# # for i in 5:
|
||||
# # test_payload.fill(i*2+10)
|
||||
# # audio_renderer.render_queue.append(test_payload)
|
||||
# test_payload.fill(0)
|
||||
# for i in 65536:
|
||||
# test_payload.set(i*2, i%256)
|
||||
# test_payload.set(i*2+1, i/256)
|
||||
# audio_renderer.render_queue.append(test_payload)
|
||||
# # audio_renderer.render_queue.append(test_payload)
|
||||
|
||||
func _process(_delta):
|
||||
update()
|
||||
|
||||
var current_rendered_bgm := 0
|
||||
func _draw() -> void:
|
||||
if audio_renderer.waiting_for_viewport:
|
||||
audio_renderer.get_result()
|
||||
var result = audio_renderer.result_queue.pop_back()
|
||||
var rendered_audio := AudioStreamSample.new()
|
||||
rendered_audio.data = result #.subarray(0, (4*120*32000) - 1)
|
||||
rendered_audio.stereo = true
|
||||
rendered_audio.mix_rate = 32000
|
||||
rendered_audio.format = AudioStreamSample.FORMAT_16_BITS
|
||||
var error = rendered_audio.save_to_wav('output/rendered_bgm_%02d.wav'%current_rendered_bgm)
|
||||
print('@%dms - Saved render of BGM%02d (error code %s)' % [Time.get_ticks_msec() - load_start_tick, current_rendered_bgm, globals.ERROR_CODE_STRINGS[error]])
|
||||
# print('@%dms - Rendered BGM%02d without saving' % [Time.get_ticks_msec() - load_start_tick, current_rendered_bgm])
|
||||
current_rendered_bgm += 1
|
||||
pass
|
||||
self._get_prerendered_audio()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
[gd_scene load_steps=7 format=2]
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://test/audio_system.gd" type="Script" id=1]
|
||||
[ext_resource path="res://theme/menu_theme.tres" type="Theme" id=2]
|
||||
[ext_resource path="res://test/audio_renderer.gd" type="Script" id=3]
|
||||
[ext_resource path="res://shaders/audio_renderer.gdshader" type="Shader" id=4]
|
||||
|
||||
|
@ -44,31 +43,45 @@ margin_right = 198.0
|
|||
margin_bottom = 176.0
|
||||
|
||||
[node name="sb_bgm" type="SpinBox" parent="."]
|
||||
margin_top = 192.0
|
||||
margin_top = 188.0
|
||||
margin_right = 38.0
|
||||
margin_bottom = 216.0
|
||||
margin_bottom = 212.0
|
||||
rect_min_size = Vector2( 38, 0 )
|
||||
max_value = 69.0
|
||||
align = 2
|
||||
|
||||
[node name="btn_bgm" type="Button" parent="."]
|
||||
[node name="btn_bgm_live" type="Button" parent="."]
|
||||
margin_left = 40.0
|
||||
margin_top = 192.0
|
||||
margin_right = 102.0
|
||||
margin_bottom = 216.0
|
||||
text = "Play BGM"
|
||||
margin_top = 188.0
|
||||
margin_right = 126.0
|
||||
margin_bottom = 212.0
|
||||
text = "Play BGM live"
|
||||
|
||||
[node name="btn_bgm_prerendered" type="Button" parent="."]
|
||||
margin_left = 132.0
|
||||
margin_top = 188.0
|
||||
margin_right = 272.0
|
||||
margin_bottom = 212.0
|
||||
text = "Play BGM prerendered"
|
||||
|
||||
[node name="btn_render" type="Button" parent="."]
|
||||
margin_left = 278.0
|
||||
margin_top = 188.0
|
||||
margin_right = 374.0
|
||||
margin_bottom = 212.0
|
||||
text = "Render All BGM"
|
||||
|
||||
[node name="btn_stop" type="Button" parent="."]
|
||||
margin_left = 320.0
|
||||
margin_top = 192.0
|
||||
margin_top = 216.0
|
||||
margin_right = 374.0
|
||||
margin_bottom = 216.0
|
||||
margin_bottom = 240.0
|
||||
text = "Stop All"
|
||||
|
||||
[node name="btn_hack_loop_extension" type="CheckBox" parent="."]
|
||||
margin_left = 105.0
|
||||
margin_top = 192.0
|
||||
margin_right = 315.0
|
||||
margin_bottom = 216.0
|
||||
theme = ExtResource( 2 )
|
||||
pressed = true
|
||||
text = "HACK: Loop Extension"
|
||||
[node name="lbl_bgm_title" type="Label" parent="."]
|
||||
margin_left = 2.0
|
||||
margin_top = 220.0
|
||||
margin_right = 42.0
|
||||
margin_bottom = 234.0
|
||||
|
||||
[node name="audio_player" type="AudioStreamPlayer" parent="."]
|
||||
|
|
Loading…
Reference in New Issue