module ZXUtils::MusicBox::Song
MusicBox
Song
¶ ↑
A song is a special Multitrack
that also organizes other multi-tracks, sub-tracks, instruments, envelopes, masks and chords.
To create a custom song you need to include the Song
module in your class.
class MySong include ZXUtils::MusicBox::Song #... song commands follow end
To compile a song just instantiate it:
mysong = MySong.new puts mysong.channel_tracks.map(&:ticks_counter) puts mysong.channel_tracks.map(&:max_recursion_depth)
To validate recursion depth of tracks and instruments do:
mysong.validate_recursion_depth!
Convert to a program class:
require 'z80' puts mysong.to_program.new(0x8000).debug
Or save as a player module:
mysong.to_player_module.save_tap('mysong')
Commands¶ ↑
For the description of available commands see MultitrackCommands
and SongCommands
.
Attributes
A hash containing compiled and used items as keys and item descriptors as values.
Public Class Methods
Creates and instance of the song.
ZXUtils::MusicBox::Multitrack::new
# File lib/zxutils/music_box/song.rb, line 170 def initialize @item_table = {} pargs = self.class.module_eval do { track_klasses: @tracks, multitrack_klasses: @multitracks, instrument_klasses: @instruments, envelopes: @envelopes, chords: @chords, masks: @masks } end super(Resolver.new @item_table, **pargs) end
Public Instance Methods
Returns a hash of instruments used in a song. Keys are instrument names and values are Instrument
instances.
# File lib/zxutils/music_box/song.rb, line 209 def instruments resolver.instruments end
Returns an instance of the SongModule
from the compiled Song
instance.
# File lib/zxutils/music_box/song.rb, line 235 def to_module body = '' track_offsets = @channel_tracks.map do |track| offset = body.bytesize body << track.code << Command::TERMINATOR offset...body.bytesize end index_offsets = @item_table.each_key.map do |item| offset = body.bytesize body << item.code << Command::TERMINATOR offset...body.bytesize end SongModule.new track_offsets, index_offsets, item_table.values, body end
Returns an instance of the PlayerModule
from the compiled Song
instance.
# File lib/zxutils/music_box/song.rb, line 224 def to_player_module to_module.to_player_module end
Returns an ad-hoc Z80::Program
class containing the compiled Song
. See SongModule.to_program
.
# File lib/zxutils/music_box/song.rb, line 230 def to_program to_module.to_program end
Returns a hash with unused item names in each of the item category.
# File lib/zxutils/music_box/song.rb, line 214 def unused_item_names { multitracks: resolver.unused_multitrack_names, tracks: resolver.unused_track_names, instruments: resolver.unused_instrument_names, envelopes: resolver.unused_envelope_names, chords: resolver.unused_chord_names, masks: resolver.unused_mask_names } end
Checks if maximal recursion depth of tracks and instruments is not exceeding the given threshold.
Provide the maximum allowed track_stack_depth
. You may want to use ZXUtils::AYMusic::TRACK_STACK_DEPTH constant.
Raises an error when recursion depth is exceeding track_stack_depth
.
# File lib/zxutils/music_box/song.rb, line 188 def validate_recursion_depth!(track_stack_depth=20) max_recursion_depth = 0 check_level = proc do |track, &block| max_recursion_depth = track.max_recursion_depth if track.max_recursion_depth > max_recursion_depth block.call if max_recursion_depth > track_stack_depth end channel_tracks.each_with_index do |track, ch_num| check_level.call(track) do raise "too many recursions on track_#{Resolver::CHANNEL_NAMES[ch_num]} depth: #{max_recursion_depth} > #{track_stack_depth}" end end instruments.each do |name, instrument| check_level.call(instrument) do raise "too many recursions on instrument :#{name} depth: #{max_recursion_depth} > #{track_stack_depth}" end end max_recursion_depth end