-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSoundManager.gd
More file actions
223 lines (171 loc) · 5.91 KB
/
Copy pathSoundManager.gd
File metadata and controls
223 lines (171 loc) · 5.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
class_name SoundManager
extends Node
var player: AudioStreamPlayer
var music_player: AudioStreamPlayer
var generator: AudioStreamGenerator
var playback: AudioStreamGeneratorPlayback
const SAMPLE_RATE = 22050 # Lower sample rate for performance (retro feel)
const PULSE_HZ = 440.0
var sfx_volume: float = 0.5
var music_volume: float = 0.5: set = set_music_volume
func _ready():
process_mode = Node.PROCESS_MODE_ALWAYS # Play even when game paused (Shop)
player = AudioStreamPlayer.new()
add_child(player)
generator = AudioStreamGenerator.new()
generator.mix_rate = SAMPLE_RATE
generator.buffer_length = 0.5
player.stream = generator
player.play()
playback = player.get_stream_playback()
setup_music()
func setup_music():
music_player = AudioStreamPlayer.new()
add_child(music_player)
var stream = load("res://assets/CrystalCascades.ogg")
if stream:
if stream is AudioStreamOggVorbis:
stream.loop = true # Ensure looping is on
music_player.stream = stream
music_player.volume_db = -10.0 # Start a bit lower so it's not blasting
# Trigger initial state
set_music_volume(music_volume)
func set_music_volume(val: float):
music_volume = val
if music_player:
# Convert linear energy (0-1) to decibels
music_player.volume_db = linear_to_db(music_volume)
# Allow muting
if music_volume <= 0.01:
if music_player.playing: music_player.stop()
else:
if not music_player.playing and music_player.stream: music_player.play()
func play_tone(freq: float, duration: float, volume: float = 0.5, wave_type: String = "square"):
if sfx_volume <= 0.01: return
if not playback: return
var frames = int(SAMPLE_RATE * duration)
var phase = 0.0
var increment = freq / SAMPLE_RATE
# Fill buffer
var buffer = PackedVector2Array()
buffer.resize(frames)
for i in range(frames):
var val = 0.0
match wave_type:
"square":
val = 1.0 if (phase < 0.5) else -1.0
"triangle":
val = 4.0 * abs(phase - 0.5) - 1.0
"sawtooth":
val = (phase * 2.0) - 1.0
"noise":
val = (randf() * 2.0) - 1.0
# Simple envelope (fade out)
var env = 1.0 - (float(i) / frames)
var sample = val * volume * env * sfx_volume
buffer[i] = Vector2(sample, sample)
phase = fmod(phase + increment, 1.0)
playback.push_buffer(buffer)
func play_slide():
# Quick Pitch Sweep (Move)
play_sweep(300, 600, 0.1)
func play_match(count: int, type = null):
# Match sound: Pitch scales with count
var base = 400 + (count * 100)
# Tile.Type enum mapping: RED=0, YELLOW=1, GREEN=2, BLUE=3, BLACK=4, PURPLE=5, ORANGE=6
var TYPE_GREEN = 2
var TYPE_BLUE = 3
var TYPE_BLACK = 4
if type == TYPE_BLUE:
# Blue: Crystal/Chime (Triangle) - Higher pitch, smooth
base = 600 + (count * 150)
play_tone(base, 0.2, 0.4, "triangle")
await get_tree().create_timer(0.05).timeout
play_tone(base * 2.0, 0.25, 0.2, "triangle")
elif type == TYPE_GREEN:
# Green: Nature/Vibrant (Sawtooth/Square mix?) - Major interval
base = 440 + (count * 100)
play_tone(base, 0.15, 0.35, "square")
await get_tree().create_timer(0.08).timeout
play_tone(base * 1.25, 0.15, 0.25, "square") # Major 3rdish
elif type == TYPE_BLACK:
# Black: Dissonant/Low (Sawtooth/Low Square) - "Less happy"
base = 150 + (count * 50)
play_tone(base, 0.3, 0.5, "sawtooth")
await get_tree().create_timer(0.05).timeout
play_tone(base * 1.41, 0.3, 0.4, "sawtooth") # Tritone-ish (diminished 5th approx)
else:
# Default (Retro Square)
play_tone(base, 0.15, 0.4, "square")
# Little echo
await get_tree().create_timer(0.1).timeout
play_tone(base * 1.5, 0.1, 0.2, "square")
func play_error():
# Low buzz
play_tone(150, 0.2, 0.5)
func play_cast():
# Powerup arpeggio
play_sweep(400, 800, 0.1)
await get_tree().create_timer(0.1).timeout
play_sweep(800, 1200, 0.2)
func play_sweep(start_freq: float, end_freq: float, duration: float, volume: float = 0.5):
if sfx_volume <= 0.01: return
if not playback: return
var frames = int(SAMPLE_RATE * duration)
var phase = 0.0
var buffer = PackedVector2Array()
buffer.resize(frames)
for i in range(frames):
var t = float(i) / frames
var current_freq = lerp(start_freq, end_freq, t)
var increment = current_freq / SAMPLE_RATE
# Sawtoothish
var val = (phase * 2.0) - 1.0
var env = 1.0 - t
var sample = val * volume * env * sfx_volume
buffer[i] = Vector2(sample, sample)
phase = fmod(phase + increment, 1.0)
playback.push_buffer(buffer)
func play_gold_tick():
# Metallic ping (high square/triangle mix)
# Frequency: ~2000Hz, very short
play_one_shot_tone(1500 + randf() * 200, 0.05, 0.3, "square")
func play_diamond_tick():
# Sparkle (high triangle)
# Frequency: ~3000Hz
play_one_shot_tone(2500 + randf() * 500, 0.05, 0.3, "triangle")
func play_one_shot_tone(freq: float, duration: float, volume: float = 0.5, wave_type: String = "square"):
if sfx_volume <= 0.01: return
var temp_player = AudioStreamPlayer.new()
add_child(temp_player)
var temp_gen = AudioStreamGenerator.new()
temp_gen.mix_rate = SAMPLE_RATE
temp_gen.buffer_length = duration + 0.1
temp_player.stream = temp_gen
temp_player.play()
var temp_playback = temp_player.get_stream_playback()
if not temp_playback:
temp_player.queue_free()
return
var frames = int(SAMPLE_RATE * duration)
var phase = 0.0
var increment = freq / SAMPLE_RATE
var buffer = PackedVector2Array()
buffer.resize(frames)
for i in range(frames):
var val = 0.0
match wave_type:
"square": val = 1.0 if (phase < 0.5) else -1.0
"triangle": val = 4.0 * abs(phase - 0.5) - 1.0
"sawtooth": val = (phase * 2.0) - 1.0
"noise": val = (randf() * 2.0) - 1.0
var env = 1.0 - (float(i) / frames)
var sample = val * volume * env * sfx_volume
buffer[i] = Vector2(sample, sample)
phase = fmod(phase + increment, 1.0)
temp_playback.push_buffer(buffer)
await get_tree().create_timer(duration + 0.1).timeout
temp_player.queue_free()
func play_ui_click():
# Simple blip
play_one_shot_tone(600, 0.05, 0.5, "triangle")