refactored sound and music

This commit is contained in:
Gordon Pedersen 2023-03-23 16:16:35 +11:00
parent 6b3ac755de
commit 4f81bab8ef
15 changed files with 326 additions and 65 deletions

View file

@ -9,6 +9,9 @@ Metrics/AbcSize:
# 10-line methods are /way/ too short
Metrics/MethodLength:
Max: 100
# As are 100 line classes
Metrics/ClassLength:
Max: 1000
# Yeah, I've got constants with mutables in them. Doesn't bother me.
Style/MutableConstant:
Enabled: false

View file

@ -5,15 +5,19 @@ require 'lib/coalesce.rb'
# then, some basic classes required for lists of assets
require 'app/sprite_instance.rb'
require 'app/sound_instance.rb'
# then, asset lists
require 'sprites/_list.rb'
require 'sounds/_list.rb'
require 'music/_list.rb'
# then, utility classes
require 'app/input.rb'
require 'app/sprite.rb'
require 'app/util.rb'
require 'app/sound.rb'
require 'app/music.rb'
require 'app/constants.rb'
require 'app/menu.rb'

View file

@ -55,7 +55,7 @@ module Menu
if args.inputs.mouse.up && args.inputs.mouse.inside_rect?(button_border)
o = options.find { |o| o[:key] == l[:key] }
play_sfx(args, :menu)
Sound.play(args, :menu)
o[:on_select].call(args) if o
end
end
@ -75,7 +75,7 @@ module Menu
menu_state.hold_delay -= 1
if menu_state.hold_delay <= 0
play_sfx(args, :menu)
Sound.play(args, :menu)
index = menu_state.current_option_i
if move == :up
index -= 1
@ -94,7 +94,7 @@ module Menu
end
if primary_down?(args.inputs)
play_sfx(args, :select)
Sound.play(args, :select)
options[menu_state.current_option_i][:on_select].call(args)
end
end

66
app/music.rb Normal file
View file

@ -0,0 +1,66 @@
# frozen_string_literal: true
# Module for managing and interacting with music.
module Music
class << self
def queue
@queue ||= {}
@queue
end
def queue_up(key, channel = 0)
@queue[channel] ||= []
@queue[channel].push(MUSIC.fetch(key))
end
def for(key)
MUSIC.fetch(key)
end
def play(args, key, opts = { channel: 0 })
MUSIC.fetch(key).play_music(args, opts)
end
def stopped(args, channel = 0)
!args.audio.key?("MUSIC_CHANNEL_#{channel}")
end
def paused(args, channel = 0)
args.audio["MUSIC_CHANNEL_#{channel}"].paused
end
def stop(args, channel = 0)
args.audio.delete("MUSIC_CHANNEL_#{channel}")
end
def pause(args, channel = 0)
args.audio["MUSIC_CHANNEL_#{channel}"].paused = true
end
def resume(args, channel = 0)
args.audio["MUSIC_CHANNEL_#{channel}"].paused = false
end
def set_volume(args, volume, channel = 0)
args.audio["MUSIC_CHANNEL_#{channel}"].gain = volume
end
def tick(args)
queue.each do |channel, value|
unless value.empty?
if args.audio["MUSIC_CHANNEL_#{channel}"]
puts "THERE'S MUSIC CURRENTLY PLAYING #{args.audio["MUSIC_CHANNEL_#{channel}"]}"
if args.audio["MUSIC_CHANNEL_#{channel}"].looping
puts "CANCEL THE LOOP ON CURRENT MUSIC"
args.audio["MUSIC_CHANNEL_#{channel}"].looping = false
end
else
puts "PLAY THAT FUNKY MUSIC! #{value}"
value.shift.play_music(args, { channel: channel, gain: args.state.setting.music ? 0.8 : 0.0 })
puts "Now, the queue is #{value}"
end
end
end
end
end
end

View file

@ -57,7 +57,7 @@ class CubeTubeGame
@lines_to_clear = []
@line_clear_timer = 0
@music_queue = []
@current_music = :music1
reset_game
end
@ -87,7 +87,8 @@ class CubeTubeGame
end
end
@music_queue = [:music1, :music2]
@current_music = :music1
Music.play(@args, @current_music)
end
def render_grid_border x, y, w, h, color
@ -257,6 +258,16 @@ class CubeTubeGame
end
end
def change_music
if @current_music == :music1
@current_music = :music2
else
@current_music = :music1
end
queue = Music.queue
Music.queue_up(@current_music)
end
def plant_current_piece
rows_to_check = []
@ -289,20 +300,20 @@ class CubeTubeGame
@line_clear_timer = 70
if (@lines%10).floor == 0
@level += 1
@args.audio[:music].looping = false
change_music
end
end
end
select_next_piece
if @lines_to_clear.empty?
play_sfx(@args, :drop)
Sound.play(@args, :drop)
if current_piece_colliding
@gameover = true
stop_music(@args)
Music.stop(@args)
end
else
play_sfx(@args, :clear)
Sound.play(@args, :clear)
@current_speed = get_speed
end
@ -330,7 +341,7 @@ class CubeTubeGame
end
def rotate_current_piece_left
play_sfx(@args, :rotate)
Sound.play(@args, :rotate)
@current_piece = @current_piece.transpose.map(&:reverse)
if(@current_piece_x + @current_piece.length) >= @grid_w
@current_piece_x = @grid_w - @current_piece.length
@ -338,7 +349,7 @@ class CubeTubeGame
end
def rotate_current_piece_right
play_sfx(@args, :rotate)
Sound.play(@args, :rotate)
@current_piece = @current_piece.transpose.map(&:reverse)
@current_piece = @current_piece.transpose.map(&:reverse)
@current_piece = @current_piece.transpose.map(&:reverse)
@ -379,13 +390,8 @@ class CubeTubeGame
return
end
if @args.audio[:music]
resume_music(@args) if @args.audio[:music].paused
@args.audio[:music].pitch = 1 + (@level * 0.125)
else
music = @music_queue.shift
@music_queue.push music
play_music(@args, music)
unless Music.stopped(@args)
Music.resume(@args) if Music.paused(@args)
end
if @line_clear_timer.positive?
@ -401,7 +407,7 @@ class CubeTubeGame
@grid[i][0] = 0
end
end
play_sfx(@args, :drop)
Sound.play(@args, :drop)
@lines_to_clear = []
end
return
@ -411,26 +417,26 @@ class CubeTubeGame
if @current_piece_x > 0
@current_piece_x -= 1
if current_piece_colliding
play_sfx(@args, :move_deny)
Sound.play(@args, :move_deny)
@current_piece_x += 1
else
play_sfx(@args, :move)
Sound.play(@args, :move)
end
else
play_sfx(@args, :move_deny)
Sound.play(@args, :move_deny)
end
end
if k.key_down.up || k.key_down.w || c.key_down.up
if (@current_piece_x + @current_piece.length) < @grid_w
@current_piece_x += 1
if current_piece_colliding
play_sfx(@args, :move_deny)
Sound.play(@args, :move_deny)
@current_piece_x -= 1
else
play_sfx(@args, :move)
Sound.play(@args, :move)
end
else
play_sfx(@args, :move_deny)
Sound.play(@args, :move_deny)
end
end
if k.key_down.left || k.key_held.left || k.key_down.a || k.key_held.a || c.key_down.left || c.key_held.left

View file

@ -27,7 +27,7 @@ module Scene
end
def pause(args)
play_sfx(args, :select)
Sound.play(args, :select)
return Scene.push(args, :paused, reset: true)
end

View file

@ -28,12 +28,10 @@ module Scene
Menu.tick(args, :paused, options)
if args.audio[:music] && !args.audio[:music].paused
pause_music(args)
end
Music.pause(args) unless Music.stopped(args)
if secondary_down?(args.inputs)
play_sfx(args, :select)
Sound.play(args, :select)
options.find { |o| o[:key] == :resume }[:on_select].call(args)
end

View file

@ -23,7 +23,7 @@ module Scene
on_select: -> (args) do
GameSetting.save_after(args) do |args|
args.state.setting.music = !args.state.setting.music
set_music_vol(args)
Music.set_volume(args, args.state.setting.music ? 0.8 : 0.0)
end
end
},
@ -50,7 +50,7 @@ module Scene
Menu.tick(args, :settings, options)
if secondary_down?(args.inputs)
play_sfx(args, :select)
Sound.play(args, :select)
options.find { |o| o[:key] == :back }[:on_select].call(args)
end

View file

@ -1,35 +1,62 @@
# play a sound effect. the file in sounds/ must match the key name. ex:
# play_sfx(args, :select)
def play_sfx(args, key)
if args.state.setting.sfx
args.outputs.sounds << "sounds/#{key}.wav"
# frozen_string_literal: true
# Module for managing and interacting with sounds.
module Sound
class << self
def for(key)
SOUNDS.fetch(key)
end
def play(args, key, opts = {})
SOUNDS.fetch(key).play(args, opts)
end
def stop(args, key)
SOUNDS.fetch(key).stop(args)
end
def pause(args, key)
SOUNDS.fetch(key).pause(args)
end
def resume(args, key)
SOUNDS.fetch(key).resume(args)
end
end
end
# play the specified music track, the key must correspond to the
# `sounds/#{key}.ogg` file naming scheme.
def play_music(args, key)
args.audio[:music] = { input: "sounds/#{key}.ogg", looping: true, }
set_music_vol(args)
end
# # play a sound effect. the file in sounds/ must match the key name. ex:
# # play_sfx(args, :select)
# def play_sfx(args, key)
# if args.state.setting.sfx
# args.outputs.sounds << "sounds/#{key}.wav"
# end
# end
# sets the music vol based on whether or not music is enabled or disabled
def set_music_vol(args)
vol = args.state.setting.music ? 0.8 : 0.0
args.audio[:music]&.gain = vol
end
# # play the specified music track, the key must correspond to the
# # `sounds/#{key}.ogg` file naming scheme.
# def play_music(args, key)
# args.audio[:music] = { input: "sounds/#{key}.ogg", looping: true, }
# set_music_vol(args)
# end
# pause the currently playing music track
def pause_music(args)
args.audio[:music].paused = true
end
# # sets the music vol based on whether or not music is enabled or disabled
# def set_music_vol(args)
# vol = args.state.setting.music ? 0.8 : 0.0
# args.audio[:music]&.gain = vol
# end
# pause the current music track
def resume_music(args)
args.audio[:music].paused = false
end
# # pause the currently playing music track
# def pause_music(args)
# args.audio[:music].paused = true
# end
# stop the currently playing music track
def stop_music(args)
args.audio.delete(:music)
end
# # pause the current music track
# def resume_music(args)
# args.audio[:music].paused = false
# end
# # stop the currently playing music track
# def stop_music(args)
# args.audio.delete(:music)
# end

134
app/sound_instance.rb Normal file
View file

@ -0,0 +1,134 @@
# frozen_string_literal: true
# class to represent sound files
class SoundInstance
def initialize(opts)
@input = opts[:input]._? opts[:path]
@key = opts[:key]._? @input
@x = opts[:x]._? 0.0
@y = opts[:y]._? 0.0
@z = opts[:z]._? 0.0
@gain = opts[:gain]._? opts[:volume]._? 1.0
@pitch = opts[:pitch]._? 1.0
@paused = opts[:paused]._? false
@looping = opts[:looping]._? false
end
attr_reader :key, :input, :x, :y, :z, :gain, :pitch, :paused, :looping
def input=(input)
args.audio[@key].input = input if args.audio[@key]
@input = input
end
def x=(xval)
args.audio[@key].x = xval if args.audio[@key]
@x = xval
end
def y=(yval)
args.audio[@key].y = yval if args.audio[@key]
@y = yval
end
def z=(zval)
args.audio[@key].z = zval if args.audio[@key]
@z = zval
end
def xyz(xval, yval, zval)
if args.audio[@key]
args.audio[@key].x = xval
args.audio[@key].y = yval
args.audio[@key].z = zval
end
@x = xval
@y = yval
@z = zval
end
def xyz=(xyz=[])
xyz(xyz[0], xyz[1], xyz[2])
end
def gain=(gain)
args.audio[@key].gain = gain if args.audio[@key]
@gain = gain
end
def pitch=(pitch)
args.audio[@key].pitch = pitch if args.audio[@key]
@pitch = pitch
end
def paused=(paused)
args.audio[@key].paused = paused if args.audio[@key]
@paused = paused
end
def looping=(looping)
args.audio[@key].looping = looping if args.audio[@key]
@looping = looping
end
def playtime
args.audio[@key].playtime._?(0.0)
end
def playtime=(playtime)
args.audio[@key].playtime = playtime
end
# convenience getter for 'path' (@input)
def path
@input
end
# convenience setter for 'path' (@input)
def path=(path)
input(path)
end
# convenience getter for 'volume' (@gain)
def volume
@gain
end
# convenience setter for 'volume' (@gain)
def volume=(volume)
gain(volume)
end
def play(args, opts = {})
obj = {
input: opts[:input]._?(opts[:path]._?(@input)),
x: opts[:x]._?(@x),
y: opts[:y]._?(@y),
z: opts[:z]._?(@z),
gain: opts[:gain]._?(opts[:volume]._?(@gain)),
pitch: opts[:pitch]._?(@pitch),
paused: opts[:paused]._?(@paused),
looping: opts[:looping]._?(@looping),
playtime: opts[:playtime]._?(0.0)
}
args.audio[@key] = obj
end
def play_music(args, opts = { channel: 0 })
@looping = true
@key = "MUSIC_CHANNEL_#{opts[:channel]}"
play(args, opts)
end
def stop(args)
args.audio.delete(@key)
end
def pause(args)
args.audio[@key].paused = true
end
def resume(args)
args.audio[@key].paused = false
end
end

View file

@ -17,6 +17,8 @@ def tick(args)
Scene.send("tick_#{args.state.scene}", args)
Music.tick(args)
debug_tick(args)
rescue FinishTick
end
@ -36,23 +38,23 @@ def debug_tick(args)
if args.inputs.keyboard.key_down.zero
play_sfx(args, :select)
Sound.play(args, :select)
args.state.render_debug_details = !args.state.render_debug_details
end
if args.inputs.keyboard.key_down.i
play_sfx(args, :select)
Sound.play(args, :select)
Sprite.reset_all(args)
args.gtk.notify!("Sprites reloaded")
end
if args.inputs.keyboard.key_down.r
play_sfx(args, :select)
Sound.play(args, :select)
$gtk.reset
end
if args.inputs.keyboard.key_down.m
play_sfx(args, :select)
Sound.play(args, :select)
args.state.simulate_mobile = !args.state.simulate_mobile
msg = if args.state.simulate_mobile
"Mobile simulation on"

8
music/_list.rb Normal file
View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
module Music
MUSIC = {
music1: SoundInstance.new({ path: 'music/music1.ogg', looping: true }),
music2: SoundInstance.new({ path: 'music/music2.ogg', looping: true })
}
end

13
sounds/_list.rb Normal file
View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Sound
SOUNDS = {
menu: SoundInstance.new({ path: 'sounds/menu.wav' }),
select: SoundInstance.new({ path: 'sounds/select.wav' }),
clear: SoundInstance.new({ path: 'sounds/clear.wav' }),
drop: SoundInstance.new({ path: 'sounds/drop.wav' }),
move: SoundInstance.new({ path: 'sounds/move.wav' }),
move_deny: SoundInstance.new({ path: 'sounds/move_deny.wav' }),
rotate: SoundInstance.new({ path: 'sounds/rotate.wav' })
}
end