2023-08-23 17:26:16 +09:30
#warning-ignore-all:shadowed_variable
extends Node
const music : = preload ( ' res://scripts/loaders/snes/music_ff5.gd ' )
const EventType : = music . EventType
var MUSIC : = music . new ( )
var inst_map : Dictionary # int keys, AudioStreamSample values
var tracks : Array
var num_tracks : int
var players : Array
var tempo : float # Store as BPM
var seconds_per_pulse : float
var master_volume : = 255
var channel_pointer : = PoolIntArray ( )
var channel_instrument_idx : = PoolByteArray ( )
var channel_next_pulse : = PoolIntArray ( )
var channel_current_note : = PoolByteArray ( )
var channel_velocity : = PoolByteArray ( )
var channel_pan : = PoolByteArray ( ) # Reversed from MIDI
var channel_octave : = PoolByteArray ( )
var channel_transpose : = PoolByteArray ( )
var channel_fine_tuning : = PoolRealArray ( )
var channel_adsr_attack : = PoolByteArray ( )
var channel_adsr_decay : = PoolByteArray ( )
var channel_adsr_sustain : = PoolByteArray ( )
var channel_adsr_release : = PoolByteArray ( )
var channel_noise_freq : = PoolByteArray ( )
var channel_noise_on : = PoolByteArray ( )
var channel_pan_lfo_rate : = PoolByteArray ( )
var channel_pan_lfo_depth : = PoolByteArray ( )
var channel_pan_lfo_on : = PoolByteArray ( )
var channel_tremolo_delay : = PoolByteArray ( )
var channel_tremolo_rate : = PoolByteArray ( )
var channel_tremolo_depth : = PoolByteArray ( )
var channel_tremolo_on : = PoolByteArray ( )
var channel_vibrato_delay : = PoolByteArray ( )
var channel_vibrato_rate : = PoolByteArray ( )
var channel_vibrato_depth : = PoolByteArray ( )
var channel_vibrato_on : = PoolByteArray ( )
var channel_pitchmod_on : = PoolByteArray ( )
var channel_echo_on : = PoolByteArray ( )
var channel_echo_volume : = PoolByteArray ( )
func set_tempo ( tempo : float ) :
self . tempo = tempo
self . seconds_per_pulse = 60.0 / ( tempo * music . PPQN )
func _init ( tracks : Array , instrument_map : Dictionary ) :
self . tracks = tracks
self . num_tracks = len ( self . tracks )
self . inst_map = instrument_map
self . players = [ ]
self . set_tempo ( 120.0 )
for i in num_tracks :
self . players . append ( AudioStreamPlayer . new ( ) )
add_child ( self . players [ - 1 ] )
self . channel_pointer . append ( 0 )
self . channel_instrument_idx . append ( 0 )
self . channel_next_pulse . append ( 0 )
self . channel_current_note . append ( 0 )
self . channel_velocity . append ( 100 )
self . channel_pan . append ( 0 )
self . channel_octave . append ( 5 )
self . channel_transpose . append ( 0 )
self . channel_fine_tuning . append ( 1.0 )
self . channel_adsr_attack . append ( 0 )
self . channel_adsr_decay . append ( 0 )
self . channel_adsr_sustain . append ( 0 )
self . channel_adsr_release . append ( 0 )
self . channel_noise_freq . append ( 0 )
self . channel_noise_on . append ( 0 )
self . channel_pan_lfo_rate . append ( 0 )
self . channel_pan_lfo_depth . append ( 0 )
self . channel_pan_lfo_on . append ( 0 )
self . channel_tremolo_delay . append ( 0 )
self . channel_tremolo_rate . append ( 0 )
self . channel_tremolo_depth . append ( 0 )
self . channel_tremolo_on . append ( 0 )
self . channel_vibrato_delay . append ( 0 )
self . channel_vibrato_rate . append ( 0 )
self . channel_vibrato_depth . append ( 0 )
self . channel_vibrato_on . append ( 0 )
self . channel_pitchmod_on . append ( 0 )
self . channel_echo_on . append ( 0 )
self . channel_echo_volume . append ( 0 )
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
2024-07-15 16:35:43 +09:30
# self.players[channel].stop()
2023-08-23 17:26:16 +09:30
var track : Array = self . tracks [ channel ]
var l : = len ( track )
var player : AudioStreamPlayer = self . players [ channel ]
while true :
var ptr : int = self . channel_pointer [ channel ]
if ptr > = l :
break
var event = track [ ptr ]
self . channel_pointer [ channel ] += 1
match event [ 0 ] : # Control codes
EventType . NOTE :
var note = event [ 1 ]
var duration = event [ 2 ]
if note > = 0 : # Don't shift or play rests
note += ( 12 * self . channel_octave [ channel ] ) + self . channel_transpose [ channel ]
player . pitch_scale = pow ( 2.0 , ( note - MUSIC . REFERENCE_NOTE ) / 12.0 ) #* self.channel_fine_tuning[channel]
player . volume_db = linear2db ( ( self . channel_velocity [ channel ] / 255.0 ) * ( self . master_volume / 255.0 ) )
2024-07-05 20:59:54 +09:30
player . play ( max ( ( SoundLoader . PLAY_START - time_offset ) / player . pitch_scale , 0 ) )
2023-08-23 17:26:16 +09:30
self . channel_current_note [ channel ] = note
2024-07-15 16:35:43 +09:30
elif note == music . NOTE_IS_TIE :
pass
2023-08-23 17:26:16 +09:30
else :
2024-07-15 16:35:43 +09:30
self . players [ channel ] . stop ( )
2023-08-23 17:26:16 +09:30
self . channel_current_note [ channel ] = - 1
# TODO: Confirm tempo scaling
return duration # Pulses to next instruction
EventType . VOLUME :
self . channel_velocity [ channel ] = event [ 1 ]
EventType . VOLUME_SLIDE : # TODO: implement slides
var slide_duration : int = event [ 1 ]
self . channel_velocity [ channel ] = event [ 2 ]
EventType . PAN : # TODO: implement slides
self . channel_pan [ channel ] = event [ 1 ]
EventType . PAN_SLIDE : # TODO: implement slides
var slide_duration : int = event [ 1 ]
self . channel_pan [ channel ] = event [ 2 ]
EventType . PITCH_SLIDE : # TODO: implement slides
var slide_duration : int = event [ 1 ]
var target_pitch : int = event [ 2 ] # Signed
EventType . VIBRATO_ON :
self . channel_vibrato_delay [ channel ] = event [ 1 ]
self . channel_vibrato_rate [ channel ] = event [ 2 ]
self . channel_vibrato_depth [ channel ] = event [ 3 ]
self . channel_vibrato_on [ channel ] = 1
EventType . VIBRATO_OFF :
self . channel_vibrato_on [ channel ] = 0
EventType . TREMOLO_ON :
self . channel_tremolo_delay [ channel ] = event [ 1 ]
self . channel_tremolo_rate [ channel ] = event [ 2 ]
self . channel_tremolo_depth [ channel ] = event [ 3 ]
self . channel_tremolo_on [ channel ] = 1
EventType . TREMOLO_OFF :
self . channel_tremolo_on [ channel ] = 0
EventType . PAN_LFO_ON :
self . channel_pan_lfo_depth [ channel ] = event [ 1 ]
self . channel_pan_lfo_rate [ channel ] = event [ 2 ]
self . channel_pan_lfo_on [ channel ] = 1
EventType . PAN_LFO_OFF :
self . channel_pan_lfo_on [ channel ] = 0
EventType . NOISE_FREQ :
self . channel_noise_freq [ channel ] = event [ 1 ]
EventType . NOISE_ON :
self . channel_noise_on [ channel ] = 1
EventType . NOISE_OFF :
self . channel_noise_on [ channel ] = 0
EventType . PITCHMOD_ON :
self . channel_pitchmod_on [ channel ] = 1
EventType . PITCHMOD_OFF :
self . channel_pitchmod_on [ channel ] = 0
EventType . ECHO_ON :
self . channel_echo_on [ channel ] = 1
EventType . ECHO_OFF :
self . channel_echo_on [ channel ] = 0
EventType . OCTAVE :
self . channel_octave [ channel ] = event [ 1 ]
EventType . OCTAVE_UP :
self . channel_octave [ channel ] += 1
EventType . OCTAVE_DOWN :
self . channel_octave [ channel ] -= 1
EventType . TRANSPOSE_ABS :
self . channel_transpose [ channel ] = event [ 1 ]
EventType . TRANSPOSE_REL :
self . channel_transpose [ channel ] += event [ 1 ]
EventType . TUNING :
var fine_tune : int = event [ 1 ]
var scale : float
if fine_tune < 0x80 :
scale = 1.0 + fine_tune / 255.0
else :
scale = fine_tune / 255.0
self . channel_fine_tuning [ channel ] = scale
EventType . PROGCHANGE :
self . channel_instrument_idx [ channel ] = event [ 1 ]
player . stream = self . inst_map [ self . channel_instrument_idx [ channel ] ]
# TODO - grab instrument envelope
EventType . ADSR_ATTACK :
self . channel_adsr_attack [ channel ] = event [ 1 ]
EventType . ADSR_DECAY :
self . channel_adsr_decay [ channel ] = event [ 1 ]
EventType . ADSR_SUSTAIN :
self . channel_adsr_sustain [ channel ] = event [ 1 ]
EventType . ADSR_RELEASE :
self . channel_adsr_release [ channel ] = event [ 1 ]
EventType . ADSR_DEFAULT : # TODO - grab instrument envelope
pass
EventType . TEMPO :
self . set_tempo ( music . tempo_to_bpm ( event [ 1 ] ) )
EventType . TEMPO_SLIDE :
self . set_tempo ( music . tempo_to_bpm ( event [ 2 ] ) )
var slide_duration : int = event [ 1 ]
EventType . ECHO_VOLUME :
self . channel_echo_volume [ channel ] = event [ 1 ]
EventType . ECHO_VOLUME_SLIDE : # TODO: implement slides
self . channel_echo_volume [ channel ] = event [ 2 ]
var slide_duration : int = event [ 1 ]
EventType . ECHO_FEEDBACK_FIR : # TODO
var feedback : int = event [ 1 ]
var filterIndex : int = event [ 2 ]
EventType . MASTER_VOLUME :
self . master_volume = event [ 1 ]
EventType . GOTO :
self . channel_pointer [ channel ] = event [ 1 ]
EventType . END :
break
_ :
break
return - 1 # End of track
func play_pulse ( time_offset : = 0.0 ) - > bool : # Return true if any channel played
var active_channels : = 0
for channel in self . num_tracks :
if self . channel_next_pulse [ channel ] < 0 :
continue # Channel not playing
active_channels += 1
if self . channel_next_pulse [ channel ] == 0 :
self . channel_next_pulse [ channel ] = self . play_channel ( channel , time_offset )
self . channel_next_pulse [ channel ] -= 1
return active_channels > 0
var is_playing : = false
var bgm_timestamp : = 0.0 # Note this will be behind by the maximum delay
func _process ( delta : float ) - > void :
if self . is_playing :
bgm_timestamp += delta
while bgm_timestamp > seconds_per_pulse :
bgm_timestamp -= seconds_per_pulse
self . is_playing = play_pulse ( bgm_timestamp )
if not self . is_playing :
print ( ' BGM finished playing ' )