From 0ef19b45a79acce71399f996a425b24e56132c9e Mon Sep 17 00:00:00 2001 From: Luke Hubmayer-Werner Date: Thu, 24 Aug 2023 14:54:32 +0930 Subject: [PATCH] Prepend silence to samples Also switch most of sample loading to struct pointers --- data/SNES_PSX_addresses.tsv | 11 +++++ scripts/loaders/RomLoader.gd | 10 +++-- scripts/loaders/SoundLoader.gd | 77 ++++++++++++++++------------------ 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/data/SNES_PSX_addresses.tsv b/data/SNES_PSX_addresses.tsv index e1d5e52..6db02db 100644 --- a/data/SNES_PSX_addresses.tsv +++ b/data/SNES_PSX_addresses.tsv @@ -3,6 +3,17 @@ character_battle_sprite_stone_palette 0x00F807 N/A N/A Palette16Of555 Also 0x199 character_battle_sprite_disabled_palette 0x00F867 /mnu/memsave.bin 0x000034 Palette16Of555 locations_bg_palettes 0x03BB00 /nar/ff5_binx.bin 0x03BF80 43 of Palette128Of555 font_glyphs_dialogue 0x03E800 256 of SNESTritile length 0x1800 +sfx_brr_data 0x041E3F Use the below SPC pointers +sfx_brr_pointers 0x041F4F 8 of 2 of u16 SPC memory addresses not ROM. Start address followed by loop address. +sfx_adsrs 0x041F71 8 of u16 +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_indices 0x043DAA 72 of 16 of u16 length 0x900 enemy_battle_sprite_palettes 0x0ED000 See enemy_battle_sprite_data for pointers. Some are 8 colours instead of 16. worldmap_blocks 0x0FF0C0 /nar/ff5_binx.bin 0x040300 3 of 4 of 192 of u8 # Top-left corners, top-right corners, bottom-left corners, bottom-right corners worldmap_tiles.bias 0x0FF9C0 /nar/ff5_bin3.bin 0x03FB00 3 of 256 of u8 Add to each pixel of the mode7c tiles diff --git a/scripts/loaders/RomLoader.gd b/scripts/loaders/RomLoader.gd index 52b8bba..7009a64 100644 --- a/scripts/loaders/RomLoader.gd +++ b/scripts/loaders/RomLoader.gd @@ -60,6 +60,9 @@ func load_snes_structs(buffer: StreamPeerBuffer) -> Dictionary: data.job_levels.append(ability_list) return data +func load_snes_audio_thread(data_and_buffer: Array): + SoundLoader.parse_rom(data_and_buffer[0], data_and_buffer[1]) + func load_snes_rom(filename: String): var error := rom_snes.open(filename, File.READ) if error == OK: @@ -71,11 +74,12 @@ func load_snes_rom(filename: String): var buffer = StreamPeerBuffer.new() buffer.data_array = bytes - StringLoader.load_snes_rom(buffer, true) - # Give this its own buffer, avoid file pointer conflicts - var _thread_error = thread.start(SoundLoader, 'parse_rom', buffer.duplicate()) self.snes_data = load_snes_structs(buffer) #print(snes_data.job_levels) + # Give this its own buffer, avoid file pointer conflicts + self.load_snes_audio_thread([self.snes_data, buffer.duplicate()]) + # var _thread_error = thread.start(self, 'load_snes_audio_thread', [self.snes_data, buffer.duplicate()]) + StringLoader.load_snes_rom(buffer, true) SpriteLoader.load_from_structs(self.snes_data) SpriteLoader.load_enemy_battle_sprites(self.snes_data, buffer) SpriteLoader.load_battle_bgs(self.snes_data, buffer) diff --git a/scripts/loaders/SoundLoader.gd b/scripts/loaders/SoundLoader.gd index 34ab993..a42148b 100644 --- a/scripts/loaders/SoundLoader.gd +++ b/scripts/loaders/SoundLoader.gd @@ -17,6 +17,8 @@ const SFX_BRR_SPC_TABLE := 0x041F4F + 2 # (first two bytes are the length of 0x0 const SFX_BRR_START := 0x041E3F + 2 # First two bytes are the length of the block, 0x010E = 270 bytes = 16 BRR packets = 480 samples const SFX_ADSR := 0x041F71 const SFX_SR := 0x041F83 +const PREPEND_MS := 50 # Prepend 20ms of silence to each sample for preplay purposes +const PLAY_START := PREPEND_MS / 1000.0 var bgm_tracks = [] var instrument_samples = [] var sfx_samples = [] @@ -96,66 +98,59 @@ func make_sample(buffer: StreamPeerBuffer, size: int, sample_rate: int) -> Audio break # Convert int array to byte array var audio_data = PoolByteArray() - for j in range(2, samples.size()): - var b = samples[j] + # Prepend silence, accounting for the two null samples + var silent_samples := ((sample_rate * PREPEND_MS) / 1000) - 2 + audio_data.resize(silent_samples * 2) # 16bit samples in 8bit array + audio_data.fill(0) + # Pack 16bit samples to 8bit array + for b in samples: audio_data.append(b & 0xFF) audio_data.append(b >> 8) audio.data = audio_data return audio -func get_inst_sample_data(buffer: StreamPeerBuffer, id: int) -> AudioStreamSample: - buffer.seek(INST_SR + (id*2)) - var tuning1 := buffer.get_u8() - var tuning2 := buffer.get_u8() - var sample_rate := get_reference_pitch_samplerate(tuning1) - - buffer.seek(INST_LOOP + (id*2)) - var loop_start_packet := buffer.get_u16()/9 # Note that Instrument $1F Steel Guitar has a length of $088B but a loop point of $088D which is 243.22... packets. Luckily it doesn't matter. - - - var lookup_offset := INST_BRR_LOOKUP + (id*3) - buffer.seek(lookup_offset) - var brr_offset := read_rom_address(buffer) - buffer.seek(brr_offset) +func get_inst_sample_data(snes_data: Dictionary, buffer: StreamPeerBuffer, id: int) -> AudioStreamSample: + var sample_rate := get_reference_pitch_samplerate(snes_data.bgm_instrument_samplerates[id] & 0xFF) + var silent_samples := ((sample_rate * PREPEND_MS) / 1000) + var loop_start_packet: int = snes_data.bgm_instrument_loop_starts[id]/9 # Note that Instrument $1F Steel Guitar has a length of $088B but a loop point of $088D which is 243.22... packets. Luckily it doesn't matter. + buffer.seek(snes_data.bgm_instrument_brr_pointers[id] & 0x3FFFFF) var size := buffer.get_u16() var num_samples := (size/9)*16 var audio := make_sample(buffer, size, sample_rate) audio.loop_mode = AudioStreamSample.LOOP_FORWARD - audio.loop_begin = loop_start_packet * 16 # Each 9byte packet is 16 samples - audio.loop_end = num_samples-1 + audio.loop_begin = (loop_start_packet * 16) + silent_samples # Each 9byte packet is 16 samples + audio.loop_end = silent_samples + num_samples - 1 # print_debug('Loaded instrument #%02X with lookup offset $%06X, BRR data offset $%06X, length $%04X (%f packets, %d samples) and loop point %d samples' % [id, lookup_offset, brr_offset, size, size/9.0, num_samples, audio.loop_begin]) return audio -func load_sfx_samples_data(buffer: StreamPeerBuffer): - var sample_rates = [] - buffer.seek(SFX_SR) - for i in SFX_NUM: - var tuning1 := buffer.get_u8() - var tuning2 := buffer.get_u8() - sample_rates.append(get_reference_pitch_samplerate(tuning1)) +func load_sfx_samples_data(snes_data: Dictionary, buffer: StreamPeerBuffer): var brr_spc_addrs = [] var brr_spc_loop_addrs = [] - buffer.seek(SFX_BRR_SPC_TABLE) + for two_of_u16 in snes_data.sfx_brr_pointers: + brr_spc_addrs.append(two_of_u16[0]) + brr_spc_loop_addrs.append(two_of_u16[1]) + var brr_spc_start = SFX_BRR_START - brr_spc_addrs[0] # Refactor this later to somehow reference sfx_brr_data address from the tsv for i in SFX_NUM: - brr_spc_addrs.append(buffer.get_u16()) - brr_spc_loop_addrs.append(buffer.get_u16()) - var brr_spc_start = brr_spc_addrs[0] - for i in SFX_NUM: - brr_spc_addrs[i] += SFX_BRR_START - brr_spc_start - for i in SFX_NUM: - buffer.seek(brr_spc_addrs[i]) + buffer.seek(brr_spc_addrs[i] + brr_spc_start) # print('Loading sfx sample #%X with BRR data offset $%06X' % [i, buffer.get_position()]) - sfx_samples.append(make_sample(buffer, 900, sample_rates[i])) # Use 900 as a limit, it won't be hit, parser stops after End packet anyway + var sample_rate := get_reference_pitch_samplerate(snes_data.sfx_samplerates[i] & 0xFF) + var silent_samples := ((sample_rate * PREPEND_MS) / 1000) + var audio := make_sample(buffer, 900, sample_rate) + var loop_start_packet: int = brr_spc_loop_addrs[i] - brr_spc_addrs[i] + audio.loop_mode = AudioStreamSample.LOOP_FORWARD + audio.loop_begin = (loop_start_packet * 16) + silent_samples # Each 9byte packet is 16 samples + audio.loop_end = (len(audio.data)/2) - 1 + sfx_samples.append(audio) # Use 900 as a limit, it won't be hit, parser stops after End packet anyway emit_signal('audio_sfx_sample_loaded', i) # print('size of %d samples' % sfx_samples[i].data.size()) # Called when the node enters the scene tree for the first time. -func load_samples(buffer: StreamPeerBuffer): - load_sfx_samples_data(buffer) +func load_samples(snes_data: Dictionary, buffer: StreamPeerBuffer): + load_sfx_samples_data(snes_data, buffer) # For some reason, this is a bit slow currently. Might optimize later. for i in INST_NUM: - instrument_samples.append(get_inst_sample_data(buffer, i)) + instrument_samples.append(get_inst_sample_data(snes_data, buffer, i)) emit_signal('audio_inst_sample_loaded', i) @@ -197,15 +192,15 @@ var player := AudioStreamPlayer.new() # Make one for each channel, later func play_sample(id: int): print('Playing inst sample #%02X' % id) player.stream = instrument_samples[id] - player.play() + player.play(PLAY_START) func play_sfx(id: int): print('Playing sfx sample #%02X' % id) player.stream = sfx_samples[id] - player.play() + player.play(PLAY_START) -func parse_rom(buffer: StreamPeerBuffer): - load_samples(buffer) +func parse_rom(snes_data: Dictionary, buffer: StreamPeerBuffer): + load_samples(snes_data, buffer) #load_bgms(buffer) has_loaded_audio_samples = true emit_signal('audio_samples_loaded')