BGM Render Shader mostly working

This commit is contained in:
Luke Hubmayer-Werner 2024-07-13 23:52:18 +09:30
parent 302eaac7ac
commit a3231c1ba9
9 changed files with 272 additions and 124 deletions

View File

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

1 Label SNES PSX_file PSX_offset format Comment
14 sfx_brr_data 0x041E41 Use the below SPC pointers
15 bytelength_sfx_brr_pointers 0x041F4F u16 Used by the memcpy routine that copies the below data to the SPC (0x0020 = 32 bytes)
16 sfx_brr_pointers 0x041F51 8 of 2 of u16 SPC memory addresses not ROM. Start address followed by loop address.
17 sfx_adsrs 0x041F71 8 of u16 8 of 4 of u4
18 sfx_samplerates 0x041F83 8 of u16
19 sfx_data 0x041F95 Contains SPC pointers and tracks
20 bgm_song_pointers 0x043B97 72 of u24
21 bgm_instrument_brr_pointers 0x043C6F 35 of u24
22 bgm_instrument_loop_starts 0x043CD8 35 of u16
23 bgm_instrument_samplerates 0x043D1E 35 of u16
24 bgm_instrument_adsrs 0x043D64 35 of u16 35 of 4 of u4
25 bgm_instrument_indices 0x043DAA 72 of 16 of u16 length 0x900
26
27 worldmap_compressed_tilesets 0x070000 tilesets 0 up to 0x434

70
data/5/bgm_titles.txt Normal file
View File

@ -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]

View File

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

View File

@ -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]]

View File

@ -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):

View File

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

View File

@ -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))

View File

@ -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()

View File

@ -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="."]