Module: Audio
- Defined in:
- ext/audio/audio.c
Class Method Summary collapse
-
.duration(clip) ⇒ Object
Audio.duration(clip) - Get duration of clip in seconds.
-
.init ⇒ Object
Audio.init - Initialize the audio engine.
-
.load(file) ⇒ Object
Audio.load(path) - Load an audio file, returns clip ID.
-
.pause(channel_id) ⇒ Object
Audio.pause(channel) - Pause playback.
-
.play(channel_id, clip) ⇒ Object
Audio.play(channel, clip) - Play a clip on a channel.
-
.resume(channel_id) ⇒ Object
Audio.resume(channel) - Resume playback.
-
.set_pitch(channel_id, pitch) ⇒ Object
Audio.set_pitch(channel, pitch) - Set pitch (1.0 = normal).
-
.set_pos(channel_id, angle, distance) ⇒ Object
distance: 0=close, 255=far.
-
.set_volume(channel_id, volume) ⇒ Object
Audio.set_volume(channel, volume) - Set volume (0-128).
-
.stop(channel_id) ⇒ Object
Audio.stop(channel) - Stop playback and rewind.
Class Method Details
.duration(clip) ⇒ Object
Audio.duration(clip) - Get duration of clip in seconds
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'ext/audio/audio.c', line 110 VALUE audio_duration(VALUE self, VALUE clip) { int clip_id = NUM2INT(clip); if (clip_id < 0 || clip_id >= sound_count || sounds[clip_id] == NULL) { rb_raise(rb_eArgError, "Invalid clip ID: %d", clip_id); return Qnil; } float length; ma_result result = ma_sound_get_length_in_seconds(sounds[clip_id], &length); if (result != MA_SUCCESS) { return Qnil; } return rb_float_new(length); } |
.init ⇒ Object
Audio.init - Initialize the audio engine
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'ext/audio/audio.c', line 283 VALUE audio_init(VALUE self) { if (engine_initialized) { return Qnil; } // Check for null driver (for CI environments without audio devices) // Usage: NATIVE_AUDIO_DRIVER=null ruby script.rb const char *driver = getenv("NATIVE_AUDIO_DRIVER"); int use_null = (driver != NULL && strcmp(driver, "null") == 0); ma_engine_config config = ma_engine_config_init(); config.listenerCount = 1; if (use_null) { ma_backend backends[] = { ma_backend_null }; ma_result ctx_result = ma_context_init(backends, 1, NULL, &context); if (ctx_result != MA_SUCCESS) { rb_raise(rb_eRuntimeError, "Failed to initialize null audio context"); return Qnil; } context_initialized = 1; using_null_backend = 1; config.pContext = &context; } ma_result result = ma_engine_init(&config, &engine); if (result != MA_SUCCESS) { if (context_initialized) { ma_context_uninit(&context); context_initialized = 0; } rb_raise(rb_eRuntimeError, "Failed to initialize audio engine"); return Qnil; } engine_initialized = 1; rb_set_end_proc(cleanup_audio, Qnil); return Qnil; } |
.load(file) ⇒ Object
Audio.load(path) - Load an audio file, returns clip ID
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'ext/audio/audio.c', line 85 VALUE audio_load(VALUE self, VALUE file) { const char *path = StringValueCStr(file); ma_sound *sound = (ma_sound *)malloc(sizeof(ma_sound)); if (sound == NULL) { rb_raise(rb_eRuntimeError, "Failed to allocate memory for sound"); return Qnil; } ma_result result = ma_sound_init_from_file(&engine, path, MA_SOUND_FLAG_DECODE, NULL, NULL, sound); if (result != MA_SUCCESS) { free(sound); rb_raise(rb_eRuntimeError, "Failed to load audio file: %s", path); return Qnil; } int id = sound_count; sounds[id] = sound; sound_count++; return rb_int2inum(id); } |
.pause(channel_id) ⇒ Object
Audio.pause(channel) - Pause playback
192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'ext/audio/audio.c', line 192 VALUE audio_pause(VALUE self, VALUE channel_id) { int channel = NUM2INT(channel_id); if (channel < 0 || channel >= MAX_CHANNELS || channels[channel] == NULL) { return Qnil; } ma_sound_stop(channels[channel]); return Qnil; } |
.play(channel_id, clip) ⇒ Object
Audio.play(channel, clip) - Play a clip on a channel
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 |
# File 'ext/audio/audio.c', line 133 VALUE audio_play(VALUE self, VALUE channel_id, VALUE clip) { int channel = NUM2INT(channel_id); int clip_id = NUM2INT(clip); if (clip_id < 0 || clip_id >= sound_count || sounds[clip_id] == NULL) { rb_raise(rb_eArgError, "Invalid clip ID: %d", clip_id); return Qnil; } if (channel < 0 || channel >= MAX_CHANNELS) { rb_raise(rb_eArgError, "Invalid channel ID: %d", channel); return Qnil; } // Clean up existing sound on this channel if (channels[channel] != NULL) { ma_sound_stop(channels[channel]); ma_sound_uninit(channels[channel]); free(channels[channel]); channels[channel] = NULL; } // Create a copy of the sound for playback ma_sound *playback = (ma_sound *)malloc(sizeof(ma_sound)); if (playback == NULL) { rb_raise(rb_eRuntimeError, "Failed to allocate memory for playback"); return Qnil; } ma_result result = ma_sound_init_copy(&engine, sounds[clip_id], 0, NULL, playback); if (result != MA_SUCCESS) { free(playback); rb_raise(rb_eRuntimeError, "Failed to create sound copy for playback"); return Qnil; } channels[channel] = playback; ma_sound_start(playback); return rb_int2inum(channel); } |
.resume(channel_id) ⇒ Object
Audio.resume(channel) - Resume playback
206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'ext/audio/audio.c', line 206 VALUE audio_resume(VALUE self, VALUE channel_id) { int channel = NUM2INT(channel_id); if (channel < 0 || channel >= MAX_CHANNELS || channels[channel] == NULL) { return Qnil; } ma_sound_start(channels[channel]); return Qnil; } |
.set_pitch(channel_id, pitch) ⇒ Object
Audio.set_pitch(channel, pitch) - Set pitch (1.0 = normal)
240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'ext/audio/audio.c', line 240 VALUE audio_set_pitch(VALUE self, VALUE channel_id, VALUE pitch) { int channel = NUM2INT(channel_id); float p = (float)NUM2DBL(pitch); if (channel < 0 || channel >= MAX_CHANNELS || channels[channel] == NULL) { return Qnil; } ma_sound_set_pitch(channels[channel], p); return Qnil; } |
.set_pos(channel_id, angle, distance) ⇒ Object
distance: 0=close, 255=far
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'ext/audio/audio.c', line 257 VALUE audio_set_pos(VALUE self, VALUE channel_id, VALUE angle, VALUE distance) { int channel = NUM2INT(channel_id); int ang = NUM2INT(angle); int dist = NUM2INT(distance); if (channel < 0 || channel >= MAX_CHANNELS || channels[channel] == NULL) { return Qnil; } // Convert polar to cartesian float rad = ang * (MA_PI / 180.0f); float normalized_dist = dist / 255.0f; float x = normalized_dist * sinf(rad); float z = -normalized_dist * cosf(rad); ma_sound_set_position(channels[channel], x, 0.0f, z); return Qnil; } |
.set_volume(channel_id, volume) ⇒ Object
Audio.set_volume(channel, volume) - Set volume (0-128)
224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'ext/audio/audio.c', line 224 VALUE audio_set_volume(VALUE self, VALUE channel_id, VALUE volume) { int channel = NUM2INT(channel_id); int vol = NUM2INT(volume); if (channel < 0 || channel >= MAX_CHANNELS || channels[channel] == NULL) { return Qnil; } float normalized_volume = vol / 128.0f; ma_sound_set_volume(channels[channel], normalized_volume); return Qnil; } |
.stop(channel_id) ⇒ Object
Audio.stop(channel) - Stop playback and rewind
177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'ext/audio/audio.c', line 177 VALUE audio_stop(VALUE self, VALUE channel_id) { int channel = NUM2INT(channel_id); if (channel < 0 || channel >= MAX_CHANNELS || channels[channel] == NULL) { return Qnil; } ma_sound_stop(channels[channel]); ma_sound_seek_to_pcm_frame(channels[channel], 0); return Qnil; } |