BGM: Add note ties (previously assumed they were also rests)

This commit is contained in:
Luke Hubmayer-Werner 2024-07-15 16:35:43 +09:30
parent a50514f7ec
commit be7874ba27
3 changed files with 47 additions and 3 deletions

View File

@ -86,7 +86,7 @@ func _init(tracks: Array, instrument_map: Dictionary):
func play_channel(channel: int, time_offset: float = 0.0) -> int:
# Executes the track events until it hits a note/rest, in which case it returns the pulse count to the next action, or the end of the events, in which case it returns -1
self.players[channel].stop()
# self.players[channel].stop()
var track: Array = self.tracks[channel]
var l := len(track)
var player: AudioStreamPlayer = self.players[channel]
@ -106,7 +106,10 @@ func play_channel(channel: int, time_offset: float = 0.0) -> int:
player.volume_db = linear2db((self.channel_velocity[channel]/255.0) * (self.master_volume/255.0))
player.play(max((SoundLoader.PLAY_START - time_offset)/player.pitch_scale, 0))
self.channel_current_note[channel] = note
elif note == music.NOTE_IS_TIE:
pass
else:
self.players[channel].stop()
self.channel_current_note[channel] = -1
# TODO: Confirm tempo scaling
return duration # Pulses to next instruction
@ -331,6 +334,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
# self.print_channel_events(inst_map)
var instrument_adsrs = RomLoader.snes_data.bgm_instrument_adsrs # TODO: UNHARDCODE THIS
var all_note_events = []
@ -412,6 +416,8 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
note_event.adsr_release = current_adsr_release
channel_note_events.append(note_event)
# num_notes += 1
elif note == music.NOTE_IS_TIE:
channel_note_events[-1].p_end += duration
p += duration
EventType.VOLUME:
var new_velocity: float = event[1]/255.0
@ -612,3 +618,32 @@ func render_channels(_t_start: float, _t_end: float, inst_map: Array) -> Array:
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]]
func print_channel_events(inst_map: Array) -> void:
for channel in self.num_tracks:
print('================Channel %d================'%channel)
var track: Array = self.tracks[channel]
var l := len(track)
var p := 0 # current pulse
for event in track: #num_notes < MAX_NOTE_EVENTS:
var print_str := 'p=%6d : %s '%[p, EventType.keys()[event[0]]]
match event[0]:
EventType.NOTE:
var note = event[1]
var duration = event[2]
match note:
music.NOTE_IS_REST:
print('p=%6d : NOTE_REST %d pulses'%[p, duration])
music.NOTE_IS_TIE:
print('p=%6d : NOTE_TIE %d pulses'%[p, duration])
_:
print(print_str, event.slice(1, -1))
p += duration
EventType.PROGCHANGE:
var event_idx = event[1]-0x20
if event_idx >= 0:
print(print_str, ' instrument %02d'%(inst_map[event_idx] - 1))
else:
print(print_str, event.slice(1, -1))
_:
print(print_str, event.slice(1, -1))

View File

@ -1,5 +1,7 @@
const MAX_TRACKS := 8
const MAX_LOOP_DEPTH := 8 # Apparently 4, but eh whatever
const NOTE_IS_TIE := -1
const NOTE_IS_REST := -2
enum EventType {
ADSR_ATTACK,
@ -126,6 +128,8 @@ static func get_int_array(size: int) -> PoolIntArray:
var EVENT_MAP: Dictionary
var NOTE_DURATIONS: PoolByteArray # This might need to be made untyped if a future addition uses PoolIntArray instead
var REFERENCE_NOTE: int
var NOTE_TIE: int
var NOTE_REST: int
const LOGGING_LEVEL_INFO: bool = false
func print_info(s: String) -> void:
@ -138,8 +142,11 @@ func translate_instruction(buffer: StreamPeer) -> Array:
if instruction < 0xD2:
var duration = self.NOTE_DURATIONS[instruction % 15]
var note = instruction / 15
if note >= 12: # 12 and 13 are rests
note = -1
if note >= 12: # 12 and 13 are rests and ties
if note == self.NOTE_REST:
note = NOTE_IS_REST
else:
note = NOTE_IS_TIE
return [EventType.NOTE, note, duration]
else:
# Control codes

View File

@ -5,6 +5,8 @@ func _init() -> void:
# Durations are in pulses, 48 = 1 quarter note (crotchet)
self.NOTE_DURATIONS = PoolByteArray([192, 144, 96, 64, 72, 48, 32, 36, 24, 16, 12, 8, 6, 4, 3]) # See ROM 0x041D7E to 0x041D8C
self.REFERENCE_NOTE = 71
self.NOTE_TIE = 12
self.NOTE_REST = 13
self.EVENT_MAP = {
0xD2: EventType.VOLUME,