audio_sdl.c (4495B)
1 #include "engine/sdl.h" 2 3 #if PH_USE_SDL 4 enum { PH_AUDIO_CLIP_MAX = 64, PH_AUDIO_EFFECT_VOICES = 8 }; 5 6 typedef struct { 7 SDL_AudioSpec spec; 8 Uint8 *data; 9 Uint32 length; 10 float gain; 11 } PhAudioClip; 12 13 struct PhAudio { 14 SDL_AudioDeviceID device; 15 SDL_AudioStream *music_stream; 16 SDL_AudioStream *effect_streams[PH_AUDIO_EFFECT_VOICES]; 17 SDL_AudioSpec device_spec; 18 PhAudioClip clips[PH_AUDIO_CLIP_MAX]; 19 int clip_count; 20 int music_id; 21 int music_enabled; 22 int effects_enabled; 23 int next_effect_voice; 24 }; 25 26 static int 27 ph_audio_stream_clip(SDL_AudioStream *stream, const PhAudioClip *clip, 28 const SDL_AudioSpec *device_spec) 29 { 30 if (!stream || !clip || !clip->data || !clip->length) return -1; 31 if (!SDL_ClearAudioStream(stream) || 32 !SDL_SetAudioStreamFormat(stream, &clip->spec, device_spec) || 33 !SDL_SetAudioStreamGain(stream, clip->gain) || 34 !SDL_PutAudioStreamData(stream, clip->data, (int)clip->length)) 35 return -1; 36 return 0; 37 } 38 39 PhAudio * 40 ph_audio_sdl_create(const PhAudioDef *defs, int count) 41 { 42 PhAudio *audio; 43 int i; 44 45 if (!defs || count <= 1 || count > PH_AUDIO_CLIP_MAX) return NULL; 46 audio = SDL_calloc(1, sizeof(*audio)); 47 if (!audio) return NULL; 48 audio->clip_count = count; 49 audio->music_enabled = 1; 50 audio->effects_enabled = 1; 51 for (i = 1; i < count; ++i) { 52 audio->clips[i].gain = defs[i].gain; 53 if (!defs[i].path || !SDL_LoadWAV(defs[i].path, &audio->clips[i].spec, 54 &audio->clips[i].data, &audio->clips[i].length)) goto fail; 55 } 56 audio->device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL); 57 if (!audio->device || !SDL_GetAudioDeviceFormat(audio->device, 58 &audio->device_spec, NULL)) goto fail; 59 audio->music_stream = SDL_CreateAudioStream(&audio->clips[1].spec, 60 &audio->device_spec); 61 if (!audio->music_stream || 62 !SDL_BindAudioStream(audio->device, audio->music_stream)) goto fail; 63 for (i = 0; i < PH_AUDIO_EFFECT_VOICES; ++i) { 64 audio->effect_streams[i] = SDL_CreateAudioStream(&audio->clips[1].spec, 65 &audio->device_spec); 66 if (!audio->effect_streams[i] || !SDL_BindAudioStream(audio->device, 67 audio->effect_streams[i])) goto fail; 68 } 69 return audio; 70 71 fail: 72 ph_audio_destroy(audio); 73 return NULL; 74 } 75 76 void 77 ph_audio_music(PhAudio *audio, int id) 78 { 79 if (!audio || id <= 0 || id >= audio->clip_count || 80 id == audio->music_id) return; 81 if (ph_audio_stream_clip(audio->music_stream, &audio->clips[id], 82 &audio->device_spec) < 0) return; 83 audio->music_id = id; 84 if (!audio->music_enabled) SDL_SetAudioStreamGain(audio->music_stream, 0.0f); 85 } 86 87 void 88 ph_audio_effect(PhAudio *audio, int id, float gain) 89 { 90 SDL_AudioStream *stream; 91 92 if (!audio || !audio->effects_enabled || 93 id <= 0 || id >= audio->clip_count || gain <= 0.0f) return; 94 stream = audio->effect_streams[audio->next_effect_voice]; 95 audio->next_effect_voice = 96 (audio->next_effect_voice + 1) % PH_AUDIO_EFFECT_VOICES; 97 if (ph_audio_stream_clip(stream, &audio->clips[id], 98 &audio->device_spec) == 0) 99 SDL_SetAudioStreamGain(stream, audio->clips[id].gain * gain); 100 } 101 102 void 103 ph_audio_set_enabled(PhAudio *audio, int music, int effects) 104 { 105 if (!audio) return; 106 music = !!music; 107 effects = !!effects; 108 if (audio->music_enabled != music) { 109 audio->music_enabled = music; 110 SDL_SetAudioStreamGain(audio->music_stream, 111 music && audio->music_id > 0 ? audio->clips[audio->music_id].gain : 0.0f); 112 } 113 if (audio->effects_enabled != effects) { 114 int i; 115 116 audio->effects_enabled = effects; 117 if (!effects) 118 for (i = 0; i < PH_AUDIO_EFFECT_VOICES; ++i) 119 SDL_ClearAudioStream(audio->effect_streams[i]); 120 } 121 } 122 123 void 124 ph_audio_update(PhAudio *audio) 125 { 126 const PhAudioClip *clip; 127 128 if (!audio || audio->music_id <= 0 || 129 audio->music_id >= audio->clip_count) return; 130 clip = &audio->clips[audio->music_id]; 131 if (SDL_GetAudioStreamQueued(audio->music_stream) < (int)clip->length / 2) 132 SDL_PutAudioStreamData(audio->music_stream, clip->data, (int)clip->length); 133 } 134 135 void 136 ph_audio_destroy(PhAudio *audio) 137 { 138 int i; 139 140 if (!audio) return; 141 if (audio->music_stream) SDL_DestroyAudioStream(audio->music_stream); 142 for (i = 0; i < PH_AUDIO_EFFECT_VOICES; ++i) 143 if (audio->effect_streams[i]) 144 SDL_DestroyAudioStream(audio->effect_streams[i]); 145 if (audio->device) SDL_CloseAudioDevice(audio->device); 146 for (i = 0; i < audio->clip_count; ++i) SDL_free(audio->clips[i].data); 147 SDL_free(audio); 148 } 149 150 void 151 ph_audio_get_enabled(const PhAudio *audio, int *music, int *effects) 152 { 153 if (music) *music = audio && audio->music_enabled; 154 if (effects) *effects = audio && audio->effects_enabled; 155 } 156 #endif